From 682c36464716e42b8f3e54c2cd008776132cc03f Mon Sep 17 00:00:00 2001 From: Mitchell Syer Date: Sun, 15 Oct 2023 20:16:30 -0400 Subject: [PATCH] Address Build Warnings and Cleanup (#707) * Address build warnings and cleanup * Actual name of who defined the protocol * Remove uneeded detekt supression * GraphQL Before-After cleanup * Lint * Cleanup unused functions * Fix some discrepancies with the 1.5 source api and fix lang exception --- server/build.gradle.kts | 10 ++++ .../tachiyomi/network/JavaScriptEngine.kt | 4 +- .../interceptor/CloudflareInterceptor.kt | 2 + .../tachiyomi/source/CatalogueSource.kt | 2 +- .../tachiyomi/source/ConfigurableSource.kt | 4 +- .../eu/kanade/tachiyomi/source/Source.kt | 11 ++-- .../tachiyomi/source/local/LocalSource.kt | 2 + .../tachiyomi/source/online/HttpSource.kt | 32 +++++++----- .../graphql/queries/CategoryQuery.kt | 29 +++-------- .../tachidesk/graphql/queries/ChapterQuery.kt | 29 +++-------- .../graphql/queries/ExtensionQuery.kt | 29 +++-------- .../tachidesk/graphql/queries/MangaQuery.kt | 29 +++-------- .../tachidesk/graphql/queries/MetaQuery.kt | 29 +++-------- .../tachidesk/graphql/queries/SourceQuery.kt | 29 +++-------- .../graphql/queries/filter/Filter.kt | 2 +- .../server/TachideskGraphQLContextFactory.kt | 6 ++- .../graphql/server/TachideskGraphQLServer.kt | 2 + .../graphql/server/primitives/OrderBy.kt | 25 +++++++++ .../ApolloSubscriptionProtocolHandler.kt | 51 +++++++------------ .../manga/impl/backup/models/Chapter.kt | 7 --- .../manga/impl/backup/models/ChapterImpl.kt | 16 ------ .../manga/impl/backup/models/Manga.kt | 16 ------ .../manga/impl/backup/models/MangaImpl.kt | 16 ------ .../manga/impl/backup/models/Track.kt | 15 ------ .../impl/backup/proto/models/BackupChapter.kt | 18 ------- .../manga/impl/download/DownloadManager.kt | 5 +- .../download/fileProvider/FileDownloader.kt | 1 + .../download/fileProvider/FileRetriever.kt | 1 + .../manga/impl/util/network/OkHttp.kt | 2 + .../suwayomi/tachidesk/server/ServerConfig.kt | 2 + .../server/util/WebInterfaceManager.kt | 3 ++ 31 files changed, 151 insertions(+), 278 deletions(-) diff --git a/server/build.gradle.kts b/server/build.gradle.kts index 868c141d6..0dbf85dd5 100644 --- a/server/build.gradle.kts +++ b/server/build.gradle.kts @@ -1,4 +1,5 @@ import de.undercouch.gradle.tasks.download.Download +import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile import java.time.Instant plugins { @@ -142,6 +143,15 @@ tasks { } } + withType { + kotlinOptions { + freeCompilerArgs += + listOf( + "-opt-in=kotlinx.serialization.ExperimentalSerializationApi", + ) + } + } + named("processResources") { duplicatesStrategy = DuplicatesStrategy.INCLUDE mustRunAfter("downloadWebUI") diff --git a/server/src/main/kotlin/eu/kanade/tachiyomi/network/JavaScriptEngine.kt b/server/src/main/kotlin/eu/kanade/tachiyomi/network/JavaScriptEngine.kt index 5e479f045..ac2e7b0c5 100644 --- a/server/src/main/kotlin/eu/kanade/tachiyomi/network/JavaScriptEngine.kt +++ b/server/src/main/kotlin/eu/kanade/tachiyomi/network/JavaScriptEngine.kt @@ -8,7 +8,9 @@ import kotlinx.coroutines.withContext /** * Util for evaluating JavaScript in sources. */ -class JavaScriptEngine(context: Context) { +class JavaScriptEngine( + @Suppress("UNUSED_PARAMETER") context: Context, +) { /** * Evaluate arbitrary JavaScript code and get the result as a primitive type * (e.g., String, Int). diff --git a/server/src/main/kotlin/eu/kanade/tachiyomi/network/interceptor/CloudflareInterceptor.kt b/server/src/main/kotlin/eu/kanade/tachiyomi/network/interceptor/CloudflareInterceptor.kt index 14addee7e..76de6d75a 100644 --- a/server/src/main/kotlin/eu/kanade/tachiyomi/network/interceptor/CloudflareInterceptor.kt +++ b/server/src/main/kotlin/eu/kanade/tachiyomi/network/interceptor/CloudflareInterceptor.kt @@ -25,6 +25,7 @@ class CloudflareInterceptor : Interceptor { private val network: NetworkHelper by injectLazy() + @Suppress("UNUSED_VARIABLE", "UNREACHABLE_CODE") override fun intercept(chain: Interceptor.Chain): Response { val originalRequest = chain.request() @@ -141,6 +142,7 @@ object CFClearance { .build() } + @Suppress("UNREACHABLE_CODE") fun getWebViewUserAgent(): String { return try { throw PlaywrightException("playwrite is diabled for v0.6.7") diff --git a/server/src/main/kotlin/eu/kanade/tachiyomi/source/CatalogueSource.kt b/server/src/main/kotlin/eu/kanade/tachiyomi/source/CatalogueSource.kt index c141aebd0..76ca73abb 100644 --- a/server/src/main/kotlin/eu/kanade/tachiyomi/source/CatalogueSource.kt +++ b/server/src/main/kotlin/eu/kanade/tachiyomi/source/CatalogueSource.kt @@ -9,7 +9,7 @@ interface CatalogueSource : Source { /** * An ISO 639-1 compliant language code (two letters in lower case). */ - val lang: String + override val lang: String /** * Whether the source has support for latest updates. diff --git a/server/src/main/kotlin/eu/kanade/tachiyomi/source/ConfigurableSource.kt b/server/src/main/kotlin/eu/kanade/tachiyomi/source/ConfigurableSource.kt index cda8f54aa..b7f412180 100644 --- a/server/src/main/kotlin/eu/kanade/tachiyomi/source/ConfigurableSource.kt +++ b/server/src/main/kotlin/eu/kanade/tachiyomi/source/ConfigurableSource.kt @@ -18,8 +18,10 @@ interface ConfigurableSource : Source { fun setupPreferenceScreen(screen: PreferenceScreen) } -private fun ConfigurableSource.preferenceKey(): String = "source_$id" +fun ConfigurableSource.preferenceKey(): String = "source_$id" // TODO: use getSourcePreferences once all extensions are on ext-lib 1.5 fun ConfigurableSource.sourcePreferences(): SharedPreferences = Injekt.get().getSharedPreferences(preferenceKey(), Context.MODE_PRIVATE) + +fun sourcePreferences(key: String): SharedPreferences = Injekt.get().getSharedPreferences(key, Context.MODE_PRIVATE) diff --git a/server/src/main/kotlin/eu/kanade/tachiyomi/source/Source.kt b/server/src/main/kotlin/eu/kanade/tachiyomi/source/Source.kt index ee7e07fca..ba13f0ae6 100644 --- a/server/src/main/kotlin/eu/kanade/tachiyomi/source/Source.kt +++ b/server/src/main/kotlin/eu/kanade/tachiyomi/source/Source.kt @@ -7,11 +7,11 @@ import rx.Observable import suwayomi.tachidesk.manga.impl.util.lang.awaitSingle /** - * A basic interface for creating a source. It could be an online source, a local source, etc... + * A basic interface for creating a source. It could be an online source, a local source, etc. */ interface Source { /** - * Id for the source. Must be unique. + * ID for the source. Must be unique. */ val id: Long @@ -20,6 +20,9 @@ interface Source { */ val name: String + val lang: String + get() = "" + /** * Get the updated details for a manga. * @@ -73,9 +76,7 @@ interface Source { "Use the non-RxJava API instead", ReplaceWith("getPageList"), ) - fun fetchPageList(chapter: SChapter): Observable> = Observable.empty() + fun fetchPageList(chapter: SChapter): Observable> = throw IllegalStateException("Not used") } // fun Source.icon(): Drawable? = Injekt.get().getAppIconForSource(this) - -fun Source.getPreferenceKey(): String = "source_$id" diff --git a/server/src/main/kotlin/eu/kanade/tachiyomi/source/local/LocalSource.kt b/server/src/main/kotlin/eu/kanade/tachiyomi/source/local/LocalSource.kt index 8dfa5fed8..0df50de22 100644 --- a/server/src/main/kotlin/eu/kanade/tachiyomi/source/local/LocalSource.kt +++ b/server/src/main/kotlin/eu/kanade/tachiyomi/source/local/LocalSource.kt @@ -32,6 +32,7 @@ import kotlinx.coroutines.withContext import kotlinx.serialization.json.Json import kotlinx.serialization.json.decodeFromStream import mu.KotlinLogging +import nl.adaptivity.xmlutil.ExperimentalXmlUtilApi import nl.adaptivity.xmlutil.core.KtXmlReader import nl.adaptivity.xmlutil.serialization.XML import org.apache.commons.compress.archivers.zip.ZipFile @@ -271,6 +272,7 @@ class LocalSource( } } + @OptIn(ExperimentalXmlUtilApi::class) private fun setMangaDetailsFromComicInfoFile( stream: InputStream, manga: SManga, diff --git a/server/src/main/kotlin/eu/kanade/tachiyomi/source/online/HttpSource.kt b/server/src/main/kotlin/eu/kanade/tachiyomi/source/online/HttpSource.kt index ab172bdb9..394577e53 100644 --- a/server/src/main/kotlin/eu/kanade/tachiyomi/source/online/HttpSource.kt +++ b/server/src/main/kotlin/eu/kanade/tachiyomi/source/online/HttpSource.kt @@ -26,18 +26,12 @@ import java.security.MessageDigest /** * A simple implementation for sources from a website. */ +@Suppress("unused") abstract class HttpSource : CatalogueSource { /** * Network service. */ - val network: NetworkHelper by injectLazy() - -// /** -// * Preferences that a source may need. -// */ -// val preferences: SharedPreferences by lazy { -// Injekt.get().getSharedPreferences(source.getPreferenceKey(), Context.MODE_PRIVATE) -// } + protected val network: NetworkHelper by injectLazy() /** * Base url of the website without the trailing slash, like: http://mysite.com @@ -60,7 +54,7 @@ abstract class HttpSource : CatalogueSource { * * Note: the generated ID sets the sign bit to `0`. */ - override val id by lazy { generateId(name, lang, versionId) } + override val id by lazy { generateId() } /** * Headers used for requests. @@ -73,6 +67,10 @@ abstract class HttpSource : CatalogueSource { open val client: OkHttpClient get() = network.client + private fun generateId(): Long { + return generateId(name, lang, versionId) + } + /** * Generates a unique ID for the source based on the provided [name], [lang] and * [versionId]. It will use the first 16 characters (64 bits) of the MD5 of the string @@ -89,6 +87,7 @@ abstract class HttpSource : CatalogueSource { * @param versionId [Int] the version ID of the source * @return a unique ID for the source */ + @Suppress("MemberVisibilityCanBePrivate") protected fun generateId( name: String, lang: String, @@ -155,8 +154,15 @@ abstract class HttpSource : CatalogueSource { query: String, filters: FilterList, ): Observable { - return client.newCall(searchMangaRequest(page, query, filters)) - .asObservableSuccess() + return Observable.defer { + try { + client.newCall(searchMangaRequest(page, query, filters)).asObservableSuccess() + } catch (e: NoClassDefFoundError) { + // RxJava doesn't handle Errors, which tends to happen during global searches + // if an old extension using non-existent classes is still around + throw RuntimeException(e) + } + } .map { response -> searchMangaParse(response) } @@ -387,7 +393,7 @@ abstract class HttpSource : CatalogueSource { * * @param page the chapter whose page list has to be fetched */ - open fun imageRequest(page: Page): Request { + protected open fun imageRequest(page: Page): Request { return GET(page.imageUrl!!, headers) } @@ -418,7 +424,7 @@ abstract class HttpSource : CatalogueSource { */ private fun getUrlWithoutDomain(orig: String): String { return try { - val uri = URI(orig) + val uri = URI(orig.replace(" ", "%20")) var out = uri.path if (uri.query != null) { out += "?" + uri.query diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/CategoryQuery.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/CategoryQuery.kt index 9d6a393c2..02714d310 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/CategoryQuery.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/CategoryQuery.kt @@ -12,15 +12,8 @@ import graphql.schema.DataFetchingEnvironment import org.jetbrains.exposed.sql.Column import org.jetbrains.exposed.sql.Op import org.jetbrains.exposed.sql.SortOrder -import org.jetbrains.exposed.sql.SortOrder.ASC -import org.jetbrains.exposed.sql.SortOrder.ASC_NULLS_FIRST -import org.jetbrains.exposed.sql.SortOrder.ASC_NULLS_LAST -import org.jetbrains.exposed.sql.SortOrder.DESC -import org.jetbrains.exposed.sql.SortOrder.DESC_NULLS_FIRST -import org.jetbrains.exposed.sql.SortOrder.DESC_NULLS_LAST import org.jetbrains.exposed.sql.SqlExpressionBuilder.greater import org.jetbrains.exposed.sql.SqlExpressionBuilder.less -import org.jetbrains.exposed.sql.andWhere import org.jetbrains.exposed.sql.selectAll import org.jetbrains.exposed.sql.transactions.transaction import suwayomi.tachidesk.graphql.queries.filter.BooleanFilter @@ -37,6 +30,7 @@ import suwayomi.tachidesk.graphql.server.primitives.Cursor import suwayomi.tachidesk.graphql.server.primitives.OrderBy import suwayomi.tachidesk.graphql.server.primitives.PageInfo import suwayomi.tachidesk.graphql.server.primitives.QueryResults +import suwayomi.tachidesk.graphql.server.primitives.applyBeforeAfter import suwayomi.tachidesk.graphql.server.primitives.greaterNotUnique import suwayomi.tachidesk.graphql.server.primitives.lessNotUnique import suwayomi.tachidesk.graphql.server.primitives.maybeSwap @@ -157,21 +151,12 @@ class CategoryQuery { val firstResult = res.firstOrNull()?.get(CategoryTable.id)?.value val lastResult = res.lastOrNull()?.get(CategoryTable.id)?.value - if (after != null) { - res.andWhere { - when (orderByType) { - DESC, DESC_NULLS_FIRST, DESC_NULLS_LAST -> (orderBy ?: CategoryOrderBy.ID).less(after) - null, ASC, ASC_NULLS_FIRST, ASC_NULLS_LAST -> (orderBy ?: CategoryOrderBy.ID).greater(after) - } - } - } else if (before != null) { - res.andWhere { - when (orderByType) { - DESC, DESC_NULLS_FIRST, DESC_NULLS_LAST -> (orderBy ?: CategoryOrderBy.ID).greater(before) - null, ASC, ASC_NULLS_FIRST, ASC_NULLS_LAST -> (orderBy ?: CategoryOrderBy.ID).less(before) - } - } - } + res.applyBeforeAfter( + before = before, + after = after, + orderBy = orderBy ?: CategoryOrderBy.ID, + orderByType = orderByType, + ) if (first != null) { res.limit(first, offset?.toLong() ?: 0) diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/ChapterQuery.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/ChapterQuery.kt index 307746516..3e0868731 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/ChapterQuery.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/ChapterQuery.kt @@ -12,18 +12,11 @@ import graphql.schema.DataFetchingEnvironment import org.jetbrains.exposed.sql.Column import org.jetbrains.exposed.sql.Op import org.jetbrains.exposed.sql.SortOrder -import org.jetbrains.exposed.sql.SortOrder.ASC -import org.jetbrains.exposed.sql.SortOrder.ASC_NULLS_FIRST -import org.jetbrains.exposed.sql.SortOrder.ASC_NULLS_LAST -import org.jetbrains.exposed.sql.SortOrder.DESC -import org.jetbrains.exposed.sql.SortOrder.DESC_NULLS_FIRST -import org.jetbrains.exposed.sql.SortOrder.DESC_NULLS_LAST import org.jetbrains.exposed.sql.SqlExpressionBuilder.greater import org.jetbrains.exposed.sql.SqlExpressionBuilder.less import org.jetbrains.exposed.sql.andWhere import org.jetbrains.exposed.sql.selectAll import org.jetbrains.exposed.sql.transactions.transaction -import suwayomi.tachidesk.graphql.queries.ChapterQuery.ChapterOrderBy.ID import suwayomi.tachidesk.graphql.queries.filter.BooleanFilter import suwayomi.tachidesk.graphql.queries.filter.Filter import suwayomi.tachidesk.graphql.queries.filter.FloatFilter @@ -40,6 +33,7 @@ import suwayomi.tachidesk.graphql.server.primitives.Cursor import suwayomi.tachidesk.graphql.server.primitives.OrderBy import suwayomi.tachidesk.graphql.server.primitives.PageInfo import suwayomi.tachidesk.graphql.server.primitives.QueryResults +import suwayomi.tachidesk.graphql.server.primitives.applyBeforeAfter import suwayomi.tachidesk.graphql.server.primitives.greaterNotUnique import suwayomi.tachidesk.graphql.server.primitives.lessNotUnique import suwayomi.tachidesk.graphql.server.primitives.maybeSwap @@ -241,21 +235,12 @@ class ChapterQuery { val firstResult = res.firstOrNull()?.get(ChapterTable.id)?.value val lastResult = res.lastOrNull()?.get(ChapterTable.id)?.value - if (after != null) { - res.andWhere { - when (orderByType) { - DESC, DESC_NULLS_FIRST, DESC_NULLS_LAST -> (orderBy ?: ID).less(after) - null, ASC, ASC_NULLS_FIRST, ASC_NULLS_LAST -> (orderBy ?: ID).greater(after) - } - } - } else if (before != null) { - res.andWhere { - when (orderByType) { - DESC, DESC_NULLS_FIRST, DESC_NULLS_LAST -> (orderBy ?: ID).greater(before) - null, ASC, ASC_NULLS_FIRST, ASC_NULLS_LAST -> (orderBy ?: ID).less(before) - } - } - } + res.applyBeforeAfter( + before = before, + after = after, + orderBy = orderBy ?: ChapterOrderBy.ID, + orderByType = orderByType, + ) if (first != null) { res.limit(first, offset?.toLong() ?: 0) diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/ExtensionQuery.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/ExtensionQuery.kt index 44141b1c7..9380ca800 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/ExtensionQuery.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/ExtensionQuery.kt @@ -13,16 +13,9 @@ import graphql.schema.DataFetchingEnvironment import org.jetbrains.exposed.sql.Column import org.jetbrains.exposed.sql.Op import org.jetbrains.exposed.sql.SortOrder -import org.jetbrains.exposed.sql.SortOrder.ASC -import org.jetbrains.exposed.sql.SortOrder.ASC_NULLS_FIRST -import org.jetbrains.exposed.sql.SortOrder.ASC_NULLS_LAST -import org.jetbrains.exposed.sql.SortOrder.DESC -import org.jetbrains.exposed.sql.SortOrder.DESC_NULLS_FIRST -import org.jetbrains.exposed.sql.SortOrder.DESC_NULLS_LAST import org.jetbrains.exposed.sql.SqlExpressionBuilder.greater import org.jetbrains.exposed.sql.SqlExpressionBuilder.less import org.jetbrains.exposed.sql.SqlExpressionBuilder.neq -import org.jetbrains.exposed.sql.andWhere import org.jetbrains.exposed.sql.selectAll import org.jetbrains.exposed.sql.transactions.transaction import suwayomi.tachidesk.graphql.queries.filter.BooleanFilter @@ -38,6 +31,7 @@ import suwayomi.tachidesk.graphql.server.primitives.Cursor import suwayomi.tachidesk.graphql.server.primitives.OrderBy import suwayomi.tachidesk.graphql.server.primitives.PageInfo import suwayomi.tachidesk.graphql.server.primitives.QueryResults +import suwayomi.tachidesk.graphql.server.primitives.applyBeforeAfter import suwayomi.tachidesk.graphql.server.primitives.greaterNotUnique import suwayomi.tachidesk.graphql.server.primitives.lessNotUnique import suwayomi.tachidesk.graphql.server.primitives.maybeSwap @@ -187,21 +181,12 @@ class ExtensionQuery { val firstResult = res.firstOrNull()?.get(ExtensionTable.pkgName) val lastResult = res.lastOrNull()?.get(ExtensionTable.pkgName) - if (after != null) { - res.andWhere { - when (orderByType) { - DESC, DESC_NULLS_FIRST, DESC_NULLS_LAST -> (orderBy ?: ExtensionOrderBy.PKG_NAME).less(after) - null, ASC, ASC_NULLS_FIRST, ASC_NULLS_LAST -> (orderBy ?: ExtensionOrderBy.PKG_NAME).greater(after) - } - } - } else if (before != null) { - res.andWhere { - when (orderByType) { - DESC, DESC_NULLS_FIRST, DESC_NULLS_LAST -> (orderBy ?: ExtensionOrderBy.PKG_NAME).greater(before) - null, ASC, ASC_NULLS_FIRST, ASC_NULLS_LAST -> (orderBy ?: ExtensionOrderBy.PKG_NAME).less(before) - } - } - } + res.applyBeforeAfter( + before = before, + after = after, + orderBy = orderBy ?: ExtensionOrderBy.PKG_NAME, + orderByType = orderByType, + ) if (first != null) { res.limit(first, offset?.toLong() ?: 0) diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/MangaQuery.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/MangaQuery.kt index 698b9ea55..34c1edde4 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/MangaQuery.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/MangaQuery.kt @@ -12,15 +12,8 @@ import graphql.schema.DataFetchingEnvironment import org.jetbrains.exposed.sql.Column import org.jetbrains.exposed.sql.Op import org.jetbrains.exposed.sql.SortOrder -import org.jetbrains.exposed.sql.SortOrder.ASC -import org.jetbrains.exposed.sql.SortOrder.ASC_NULLS_FIRST -import org.jetbrains.exposed.sql.SortOrder.ASC_NULLS_LAST -import org.jetbrains.exposed.sql.SortOrder.DESC -import org.jetbrains.exposed.sql.SortOrder.DESC_NULLS_FIRST -import org.jetbrains.exposed.sql.SortOrder.DESC_NULLS_LAST import org.jetbrains.exposed.sql.SqlExpressionBuilder.greater import org.jetbrains.exposed.sql.SqlExpressionBuilder.less -import org.jetbrains.exposed.sql.andWhere import org.jetbrains.exposed.sql.selectAll import org.jetbrains.exposed.sql.transactions.transaction import suwayomi.tachidesk.graphql.queries.filter.BooleanFilter @@ -39,6 +32,7 @@ import suwayomi.tachidesk.graphql.server.primitives.Cursor import suwayomi.tachidesk.graphql.server.primitives.OrderBy import suwayomi.tachidesk.graphql.server.primitives.PageInfo import suwayomi.tachidesk.graphql.server.primitives.QueryResults +import suwayomi.tachidesk.graphql.server.primitives.applyBeforeAfter import suwayomi.tachidesk.graphql.server.primitives.greaterNotUnique import suwayomi.tachidesk.graphql.server.primitives.lessNotUnique import suwayomi.tachidesk.graphql.server.primitives.maybeSwap @@ -245,21 +239,12 @@ class MangaQuery { val firstResult = res.firstOrNull()?.get(MangaTable.id)?.value val lastResult = res.lastOrNull()?.get(MangaTable.id)?.value - if (after != null) { - res.andWhere { - when (orderByType) { - DESC, DESC_NULLS_FIRST, DESC_NULLS_LAST -> (orderBy ?: MangaOrderBy.ID).less(after) - null, ASC, ASC_NULLS_FIRST, ASC_NULLS_LAST -> (orderBy ?: MangaOrderBy.ID).greater(after) - } - } - } else if (before != null) { - res.andWhere { - when (orderByType) { - DESC, DESC_NULLS_FIRST, DESC_NULLS_LAST -> (orderBy ?: MangaOrderBy.ID).greater(before) - null, ASC, ASC_NULLS_FIRST, ASC_NULLS_LAST -> (orderBy ?: MangaOrderBy.ID).less(before) - } - } - } + res.applyBeforeAfter( + before = before, + after = after, + orderBy = orderBy ?: MangaOrderBy.ID, + orderByType = orderByType, + ) if (first != null) { res.limit(first, offset?.toLong() ?: 0) diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/MetaQuery.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/MetaQuery.kt index 15d5adf06..160d00c67 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/MetaQuery.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/MetaQuery.kt @@ -12,15 +12,8 @@ import graphql.schema.DataFetchingEnvironment import org.jetbrains.exposed.sql.Column import org.jetbrains.exposed.sql.Op import org.jetbrains.exposed.sql.SortOrder -import org.jetbrains.exposed.sql.SortOrder.ASC -import org.jetbrains.exposed.sql.SortOrder.ASC_NULLS_FIRST -import org.jetbrains.exposed.sql.SortOrder.ASC_NULLS_LAST -import org.jetbrains.exposed.sql.SortOrder.DESC -import org.jetbrains.exposed.sql.SortOrder.DESC_NULLS_FIRST -import org.jetbrains.exposed.sql.SortOrder.DESC_NULLS_LAST import org.jetbrains.exposed.sql.SqlExpressionBuilder.greater import org.jetbrains.exposed.sql.SqlExpressionBuilder.less -import org.jetbrains.exposed.sql.andWhere import org.jetbrains.exposed.sql.selectAll import org.jetbrains.exposed.sql.transactions.transaction import suwayomi.tachidesk.global.model.table.GlobalMetaTable @@ -34,6 +27,7 @@ import suwayomi.tachidesk.graphql.server.primitives.Cursor import suwayomi.tachidesk.graphql.server.primitives.OrderBy import suwayomi.tachidesk.graphql.server.primitives.PageInfo import suwayomi.tachidesk.graphql.server.primitives.QueryResults +import suwayomi.tachidesk.graphql.server.primitives.applyBeforeAfter import suwayomi.tachidesk.graphql.server.primitives.greaterNotUnique import suwayomi.tachidesk.graphql.server.primitives.lessNotUnique import suwayomi.tachidesk.graphql.server.primitives.maybeSwap @@ -141,21 +135,12 @@ class MetaQuery { val firstResult = res.firstOrNull()?.get(GlobalMetaTable.key) val lastResult = res.lastOrNull()?.get(GlobalMetaTable.key) - if (after != null) { - res.andWhere { - when (orderByType) { - DESC, DESC_NULLS_FIRST, DESC_NULLS_LAST -> (orderBy ?: MetaOrderBy.KEY).less(after) - null, ASC, ASC_NULLS_FIRST, ASC_NULLS_LAST -> (orderBy ?: MetaOrderBy.KEY).greater(after) - } - } - } else if (before != null) { - res.andWhere { - when (orderByType) { - DESC, DESC_NULLS_FIRST, DESC_NULLS_LAST -> (orderBy ?: MetaOrderBy.KEY).greater(before) - null, ASC, ASC_NULLS_FIRST, ASC_NULLS_LAST -> (orderBy ?: MetaOrderBy.KEY).less(before) - } - } - } + res.applyBeforeAfter( + before = before, + after = after, + orderBy = orderBy ?: MetaOrderBy.KEY, + orderByType = orderByType, + ) if (first != null) { res.limit(first, offset?.toLong() ?: 0) diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/SourceQuery.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/SourceQuery.kt index a98fc9bbb..116932cd5 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/SourceQuery.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/SourceQuery.kt @@ -12,15 +12,8 @@ import graphql.schema.DataFetchingEnvironment import org.jetbrains.exposed.sql.Column import org.jetbrains.exposed.sql.Op import org.jetbrains.exposed.sql.SortOrder -import org.jetbrains.exposed.sql.SortOrder.ASC -import org.jetbrains.exposed.sql.SortOrder.ASC_NULLS_FIRST -import org.jetbrains.exposed.sql.SortOrder.ASC_NULLS_LAST -import org.jetbrains.exposed.sql.SortOrder.DESC -import org.jetbrains.exposed.sql.SortOrder.DESC_NULLS_FIRST -import org.jetbrains.exposed.sql.SortOrder.DESC_NULLS_LAST import org.jetbrains.exposed.sql.SqlExpressionBuilder.greater import org.jetbrains.exposed.sql.SqlExpressionBuilder.less -import org.jetbrains.exposed.sql.andWhere import org.jetbrains.exposed.sql.selectAll import org.jetbrains.exposed.sql.transactions.transaction import suwayomi.tachidesk.graphql.queries.filter.BooleanFilter @@ -37,6 +30,7 @@ import suwayomi.tachidesk.graphql.server.primitives.Cursor import suwayomi.tachidesk.graphql.server.primitives.OrderBy import suwayomi.tachidesk.graphql.server.primitives.PageInfo import suwayomi.tachidesk.graphql.server.primitives.QueryResults +import suwayomi.tachidesk.graphql.server.primitives.applyBeforeAfter import suwayomi.tachidesk.graphql.server.primitives.greaterNotUnique import suwayomi.tachidesk.graphql.server.primitives.lessNotUnique import suwayomi.tachidesk.graphql.server.primitives.maybeSwap @@ -157,21 +151,12 @@ class SourceQuery { val firstResult = res.firstOrNull()?.get(SourceTable.id)?.value val lastResult = res.lastOrNull()?.get(SourceTable.id)?.value - if (after != null) { - res.andWhere { - when (orderByType) { - DESC, DESC_NULLS_FIRST, DESC_NULLS_LAST -> (orderBy ?: SourceOrderBy.ID).less(after) - null, ASC, ASC_NULLS_FIRST, ASC_NULLS_LAST -> (orderBy ?: SourceOrderBy.ID).greater(after) - } - } - } else if (before != null) { - res.andWhere { - when (orderByType) { - DESC, DESC_NULLS_FIRST, DESC_NULLS_LAST -> (orderBy ?: SourceOrderBy.ID).greater(before) - null, ASC, ASC_NULLS_FIRST, ASC_NULLS_LAST -> (orderBy ?: SourceOrderBy.ID).less(before) - } - } - } + res.applyBeforeAfter( + before = before, + after = after, + orderBy = orderBy ?: SourceOrderBy.ID, + orderByType = orderByType, + ) if (first != null) { res.limit(first, offset?.toLong() ?: 0) diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/filter/Filter.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/filter/Filter.kt index b81ab72c1..a30d2aa1f 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/filter/Filter.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/filter/Filter.kt @@ -313,7 +313,7 @@ data class StringListFilter( val hasNoneInsensitive: List? = null, ) : ListScalarFilter> -@Suppress("UNCHECKED_CAST") +@Suppress("UNCHECKED_CAST", "FINAL_UPPER_BOUND") fun andFilterWithCompareString( column: Column, filter: StringFilter?, diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/TachideskGraphQLContextFactory.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/TachideskGraphQLContextFactory.kt index 4eed39b9c..b5bc5ed61 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/TachideskGraphQLContextFactory.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/TachideskGraphQLContextFactory.kt @@ -5,6 +5,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +@file:Suppress("DEPRECATION") + package suwayomi.tachidesk.graphql.server import com.expediagroup.graphql.generator.execution.GraphQLContext @@ -30,7 +32,9 @@ class TachideskGraphQLContextFactory : GraphQLContextFactory = emptyMap() + fun generateContextMap( + @Suppress("UNUSED_PARAMETER") request: WsContext, + ): Map<*, Any> = emptyMap() } /** diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/TachideskGraphQLServer.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/TachideskGraphQLServer.kt index 3f2c84c9d..05cca0e8b 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/TachideskGraphQLServer.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/TachideskGraphQLServer.kt @@ -15,6 +15,7 @@ import graphql.GraphQL import io.javalin.http.Context import io.javalin.websocket.WsCloseContext import io.javalin.websocket.WsMessageContext +import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map @@ -30,6 +31,7 @@ class TachideskGraphQLServer( private val objectMapper = jacksonObjectMapper() private val subscriptionProtocolHandler = ApolloSubscriptionProtocolHandler(contextFactory, subscriptionHandler, objectMapper) + @OptIn(DelicateCoroutinesApi::class) fun handleSubscriptionMessage(context: WsMessageContext) { subscriptionProtocolHandler.handleMessage(context) .map { objectMapper.writeValueAsString(it) } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/primitives/OrderBy.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/primitives/OrderBy.kt index d267fb116..e15e792b2 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/primitives/OrderBy.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/primitives/OrderBy.kt @@ -3,11 +3,13 @@ package suwayomi.tachidesk.graphql.server.primitives import org.jetbrains.exposed.dao.id.EntityID import org.jetbrains.exposed.sql.Column import org.jetbrains.exposed.sql.Op +import org.jetbrains.exposed.sql.Query import org.jetbrains.exposed.sql.SortOrder import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq import org.jetbrains.exposed.sql.SqlExpressionBuilder.greater import org.jetbrains.exposed.sql.SqlExpressionBuilder.less import org.jetbrains.exposed.sql.and +import org.jetbrains.exposed.sql.andWhere import org.jetbrains.exposed.sql.or interface OrderBy { @@ -36,6 +38,29 @@ fun SortOrder?.maybeSwap(value: Any?): SortOrder { } } +fun Query.applyBeforeAfter( + before: Cursor?, + after: Cursor?, + orderBy: OrderBy, + orderByType: SortOrder?, +) { + if (after != null) { + andWhere { + when (orderByType) { + SortOrder.DESC, SortOrder.DESC_NULLS_FIRST, SortOrder.DESC_NULLS_LAST -> orderBy.less(after) + null, SortOrder.ASC, SortOrder.ASC_NULLS_FIRST, SortOrder.ASC_NULLS_LAST -> orderBy.greater(after) + } + } + } else if (before != null) { + andWhere { + when (orderByType) { + SortOrder.DESC, SortOrder.DESC_NULLS_FIRST, SortOrder.DESC_NULLS_LAST -> orderBy.greater(before) + null, SortOrder.ASC, SortOrder.ASC_NULLS_FIRST, SortOrder.ASC_NULLS_LAST -> orderBy.less(before) + } + } + } +} + @JvmName("greaterNotUniqueIntKey") fun > greaterNotUnique( column: Column, diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/subscriptions/ApolloSubscriptionProtocolHandler.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/subscriptions/ApolloSubscriptionProtocolHandler.kt index 3d4e5a773..15dc9941e 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/subscriptions/ApolloSubscriptionProtocolHandler.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/subscriptions/ApolloSubscriptionProtocolHandler.kt @@ -38,8 +38,8 @@ import suwayomi.tachidesk.graphql.server.toGraphQLContext import suwayomi.tachidesk.server.serverConfig /** - * Implementation of the `graphql-ws` protocol defined by Apollo - * https://github.com/apollographql/subscriptions-transport-ws/blob/master/PROTOCOL.md + * Implementation of the `graphql-transport-ws` protocol defined by Denis Badurina + * https://github.com/enisdenjo/graphql-ws/blob/master/PROTOCOL.md * ported for Javalin */ class ApolloSubscriptionProtocolHandler( @@ -47,6 +47,10 @@ class ApolloSubscriptionProtocolHandler( private val subscriptionHandler: GraphQLSubscriptionHandler, private val objectMapper: ObjectMapper, ) { + companion object { + private const val UNKNOWN_OPERATION_NAME = "__UNKNOWN__" + } + private val sessionState = ApolloSubscriptionSessionState() private val logger = KotlinLogging.logger {} private val pongMessage = SubscriptionOperationMessage(type = GQL_PONG.type) @@ -54,14 +58,10 @@ class ApolloSubscriptionProtocolHandler( private val acknowledgeMessage = SubscriptionOperationMessage(GQL_CONNECTION_ACK.type) private fun getOperationName(payload: Any?): String { - val unknownOperationName = "__UNKNOWN__" - - try { - @Suppress("UNCHECKED_CAST") - return (payload as Map)["operationName"] ?: unknownOperationName - } catch (e: Exception) { - return unknownOperationName - } + @Suppress("UNCHECKED_CAST") + return (payload as? Map) + .orEmpty() + .getOrDefault("operationName", UNKNOWN_OPERATION_NAME) } fun handleMessage(context: WsMessageContext): Flow { @@ -83,12 +83,12 @@ class ApolloSubscriptionProtocolHandler( return try { when (operationMessage.type) { - GQL_CONNECTION_INIT.type -> onInit(operationMessage, context) + GQL_CONNECTION_INIT.type -> onInit(context) GQL_SUBSCRIBE.type -> startSubscription(operationMessage, context) - GQL_COMPLETE.type -> onComplete(operationMessage, context) + GQL_COMPLETE.type -> onComplete(operationMessage) GQL_PING.type -> onPing() GQL_PONG.type -> emptyFlow() - else -> onUnknownOperation(operationMessage, context) + else -> onUnknownOperation(operationMessage) } } catch (exception: Exception) { onException(exception) @@ -108,7 +108,6 @@ class ApolloSubscriptionProtocolHandler( } } - @Suppress("Detekt.TooGenericExceptionCaught") private fun startSubscription( operationMessage: SubscriptionOperationMessage, context: WsContext, @@ -142,7 +141,7 @@ class ApolloSubscriptionProtocolHandler( SubscriptionOperationMessage(type = GQL_NEXT.type, id = operationMessage.id, payload = it) } } - .onCompletion { if (it == null) emitAll(onComplete(operationMessage, context)) } + .onCompletion { if (it == null) emitAll(onComplete(operationMessage)) } .onStart { sessionState.saveOperation(context, operationMessage, currentCoroutineContext().job) } } catch (exception: Exception) { logger.error("Error running graphql subscription", exception) @@ -152,21 +151,15 @@ class ApolloSubscriptionProtocolHandler( } } - private fun onInit( - operationMessage: SubscriptionOperationMessage, - context: WsContext, - ): Flow { - saveContext(operationMessage, context) + private fun onInit(context: WsContext): Flow { + saveContext(context) return flowOf(acknowledgeMessage) } /** * Generate the context and save it for all future messages. */ - private fun saveContext( - operationMessage: SubscriptionOperationMessage, - context: WsContext, - ) { + private fun saveContext(context: WsContext) { runBlocking { val graphQLContext = contextFactory.generateContextMap(context).toGraphQLContext() sessionState.saveContext(context, graphQLContext) @@ -176,10 +169,7 @@ class ApolloSubscriptionProtocolHandler( /** * Called with the publisher has completed on its own. */ - private fun onComplete( - operationMessage: SubscriptionOperationMessage, - context: WsContext, - ): Flow { + private fun onComplete(operationMessage: SubscriptionOperationMessage): Flow { return sessionState.completeOperation(operationMessage) } @@ -192,10 +182,7 @@ class ApolloSubscriptionProtocolHandler( return emptyFlow() } - private fun onUnknownOperation( - operationMessage: SubscriptionOperationMessage, - context: WsContext, - ): Flow { + private fun onUnknownOperation(operationMessage: SubscriptionOperationMessage): Flow { logger.error("Unknown subscription operation $operationMessage") sessionState.completeOperation(operationMessage) return emptyFlow() diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/models/Chapter.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/models/Chapter.kt index 42cc20fb7..fc24246f2 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/models/Chapter.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/models/Chapter.kt @@ -22,11 +22,4 @@ interface Chapter : SChapter, Serializable { val isRecognizedNumber: Boolean get() = chapter_number >= 0f - - companion object { - fun create(): Chapter = - ChapterImpl().apply { - chapter_number = -1f - } - } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/models/ChapterImpl.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/models/ChapterImpl.kt index c218b2c18..82f743ea1 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/models/ChapterImpl.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/models/ChapterImpl.kt @@ -2,9 +2,6 @@ package suwayomi.tachidesk.manga.impl.backup.models -import org.jetbrains.exposed.sql.ResultRow -import suwayomi.tachidesk.manga.model.table.ChapterTable - class ChapterImpl : Chapter { override var id: Long? = null @@ -42,17 +39,4 @@ class ChapterImpl : Chapter { override fun hashCode(): Int { return url.hashCode() + id.hashCode() } - - // Tachidesk --> - companion object { - fun fromQuery(chapterRecord: ResultRow): ChapterImpl { - return ChapterImpl().apply { - url = chapterRecord[ChapterTable.url] - read = chapterRecord[ChapterTable.isRead] - bookmark = chapterRecord[ChapterTable.isBookmarked] - last_page_read = chapterRecord[ChapterTable.lastPageRead] - } - } - } - // Tachidesk <-- } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/models/Manga.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/models/Manga.kt index 5b9886e0a..50b868c84 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/models/Manga.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/models/Manga.kt @@ -120,22 +120,6 @@ interface Manga : SManga { const val CHAPTER_DISPLAY_NAME = 0x00000000 const val CHAPTER_DISPLAY_NUMBER = 0x00100000 const val CHAPTER_DISPLAY_MASK = 0x00100000 - - fun create(source: Long): Manga = - MangaImpl().apply { - this.source = source - } - - fun create( - pathUrl: String, - title: String, - source: Long = 0, - ): Manga = - MangaImpl().apply { - url = pathUrl - this.title = title - this.source = source - } } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/models/MangaImpl.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/models/MangaImpl.kt index c24e6ab3c..45a97bc5c 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/models/MangaImpl.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/models/MangaImpl.kt @@ -3,8 +3,6 @@ package suwayomi.tachidesk.manga.impl.backup.models import eu.kanade.tachiyomi.source.model.UpdateStrategy -import org.jetbrains.exposed.sql.ResultRow -import suwayomi.tachidesk.manga.model.table.MangaTable open class MangaImpl : Manga { override var id: Long? = null @@ -68,18 +66,4 @@ open class MangaImpl : Manga { override fun hashCode(): Int { return url.hashCode() + id.hashCode() } - - // Tachidesk --> - companion object { - fun fromQuery(mangaRecord: ResultRow): MangaImpl { - return MangaImpl().apply { - url = mangaRecord[MangaTable.url] - title = mangaRecord[MangaTable.title] - source = mangaRecord[MangaTable.sourceReference] - viewer_flags = 0 // TODO: implement - chapter_flags = 0 // TODO: implement - } - } - } - // Tachidesk <-- } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/models/Track.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/models/Track.kt index 685912546..f0a0c995b 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/models/Track.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/models/Track.kt @@ -30,19 +30,4 @@ interface Track : Serializable { var finished_reading_date: Long var tracking_url: String - - fun copyPersonalFrom(other: Track) { - last_chapter_read = other.last_chapter_read - score = other.score - status = other.status - started_reading_date = other.started_reading_date - finished_reading_date = other.finished_reading_date - } - - companion object { - fun create(serviceId: Int): Track = - TrackImpl().apply { - sync_id = serviceId - } - } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/proto/models/BackupChapter.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/proto/models/BackupChapter.kt index bdc60b374..30cc0acd4 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/proto/models/BackupChapter.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/proto/models/BackupChapter.kt @@ -2,7 +2,6 @@ package suwayomi.tachidesk.manga.impl.backup.proto.models import kotlinx.serialization.Serializable import kotlinx.serialization.protobuf.ProtoNumber -import suwayomi.tachidesk.manga.impl.backup.models.Chapter import suwayomi.tachidesk.manga.impl.backup.models.ChapterImpl @Serializable @@ -36,21 +35,4 @@ data class BackupChapter( source_order = this@BackupChapter.sourceOrder } } - - companion object { - fun copyFrom(chapter: Chapter): BackupChapter { - return BackupChapter( - url = chapter.url, - name = chapter.name, - chapterNumber = chapter.chapter_number, - scanlator = chapter.scanlator, - read = chapter.read, - bookmark = chapter.bookmark, - lastPageRead = chapter.last_page_read, - dateFetch = chapter.date_fetch, - dateUpload = chapter.date_upload, - sourceOrder = chapter.source_order, - ) - } - } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/DownloadManager.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/DownloadManager.kt index 205d0c297..4487ced5c 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/DownloadManager.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/DownloadManager.kt @@ -13,6 +13,7 @@ import io.javalin.websocket.WsContext import io.javalin.websocket.WsMessageContext import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll @@ -51,6 +52,7 @@ import kotlin.time.Duration.Companion.seconds private val logger = KotlinLogging.logger {} +@OptIn(FlowPreview::class) object DownloadManager { private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Default) private val clients = ConcurrentHashMap() @@ -150,9 +152,6 @@ object DownloadManager { if (immediate) { sendStatusToAllClients() } - /*if (downloadChapter != null) { TODO GRAPHQL - downloadSubscriptionSource.publish(downloadChapter) - }*/ } private fun getStatus(): DownloadStatus { diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/fileProvider/FileDownloader.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/fileProvider/FileDownloader.kt index f57d85a0a..aaf86e563 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/fileProvider/FileDownloader.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/fileProvider/FileDownloader.kt @@ -12,6 +12,7 @@ fun interface FileDownload0Args : FileDownload { } } +@Suppress("UNCHECKED_CAST") fun interface FileDownload3Args : FileDownload { suspend fun execute( a: A, diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/fileProvider/FileRetriever.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/fileProvider/FileRetriever.kt index 4549e4bfc..b8d881d7d 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/fileProvider/FileRetriever.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/fileProvider/FileRetriever.kt @@ -14,6 +14,7 @@ fun interface RetrieveFile0Args : RetrieveFile { } } +@Suppress("UNCHECKED_CAST") fun interface RetrieveFile1Args : RetrieveFile { fun execute(a: A): Pair diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/util/network/OkHttp.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/util/network/OkHttp.kt index 5feb91209..3d20ac0ed 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/util/network/OkHttp.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/util/network/OkHttp.kt @@ -7,6 +7,7 @@ package suwayomi.tachidesk.manga.impl.util.network * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.suspendCancellableCoroutine import okhttp3.Call import okhttp3.Callback @@ -16,6 +17,7 @@ import java.io.IOException import kotlin.coroutines.resumeWithException // Based on https://github.com/gildor/kotlin-coroutines-okhttp +@OptIn(ExperimentalCoroutinesApi::class) suspend fun Call.await(): Response { return suspendCancellableCoroutine { continuation -> enqueue( diff --git a/server/src/main/kotlin/suwayomi/tachidesk/server/ServerConfig.kt b/server/src/main/kotlin/suwayomi/tachidesk/server/ServerConfig.kt index 407f7aae1..91e41feb2 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/server/ServerConfig.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/server/ServerConfig.kt @@ -10,6 +10,7 @@ package suwayomi.tachidesk.server import com.typesafe.config.Config import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.Flow @@ -111,6 +112,7 @@ class ServerConfig(getConfig: () -> Config, val moduleName: String = SERVER_CONF // local source val localSourcePath: MutableStateFlow by OverrideConfigValue(StringConfigAdapter) + @OptIn(ExperimentalCoroutinesApi::class) fun subscribeTo( flow: Flow, onChange: suspend (value: T) -> Unit, diff --git a/server/src/main/kotlin/suwayomi/tachidesk/server/util/WebInterfaceManager.kt b/server/src/main/kotlin/suwayomi/tachidesk/server/util/WebInterfaceManager.kt index 94eeea4b8..619e24933 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/server/util/WebInterfaceManager.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/server/util/WebInterfaceManager.kt @@ -12,6 +12,7 @@ import eu.kanade.tachiyomi.network.NetworkHelper import eu.kanade.tachiyomi.network.awaitSuccess import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.channels.BufferOverflow.DROP_OLDEST import kotlinx.coroutines.flow.MutableSharedFlow @@ -131,6 +132,8 @@ object WebInterfaceManager { private val notifyFlow = MutableSharedFlow(extraBufferCapacity = 1, onBufferOverflow = DROP_OLDEST) + + @OptIn(FlowPreview::class) val status = notifyFlow.sample(1.seconds) .stateIn(