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
This commit is contained in:
Mitchell Syer
2023-10-15 20:16:30 -04:00
committed by GitHub
parent e70730e9a8
commit 682c364647
31 changed files with 151 additions and 278 deletions

View File

@@ -1,4 +1,5 @@
import de.undercouch.gradle.tasks.download.Download import de.undercouch.gradle.tasks.download.Download
import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile
import java.time.Instant import java.time.Instant
plugins { plugins {
@@ -142,6 +143,15 @@ tasks {
} }
} }
withType<KotlinJvmCompile> {
kotlinOptions {
freeCompilerArgs +=
listOf(
"-opt-in=kotlinx.serialization.ExperimentalSerializationApi",
)
}
}
named<Copy>("processResources") { named<Copy>("processResources") {
duplicatesStrategy = DuplicatesStrategy.INCLUDE duplicatesStrategy = DuplicatesStrategy.INCLUDE
mustRunAfter("downloadWebUI") mustRunAfter("downloadWebUI")

View File

@@ -8,7 +8,9 @@ import kotlinx.coroutines.withContext
/** /**
* Util for evaluating JavaScript in sources. * 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 * Evaluate arbitrary JavaScript code and get the result as a primitive type
* (e.g., String, Int). * (e.g., String, Int).

View File

@@ -25,6 +25,7 @@ class CloudflareInterceptor : Interceptor {
private val network: NetworkHelper by injectLazy() private val network: NetworkHelper by injectLazy()
@Suppress("UNUSED_VARIABLE", "UNREACHABLE_CODE")
override fun intercept(chain: Interceptor.Chain): Response { override fun intercept(chain: Interceptor.Chain): Response {
val originalRequest = chain.request() val originalRequest = chain.request()
@@ -141,6 +142,7 @@ object CFClearance {
.build() .build()
} }
@Suppress("UNREACHABLE_CODE")
fun getWebViewUserAgent(): String { fun getWebViewUserAgent(): String {
return try { return try {
throw PlaywrightException("playwrite is diabled for v0.6.7") throw PlaywrightException("playwrite is diabled for v0.6.7")

View File

@@ -9,7 +9,7 @@ interface CatalogueSource : Source {
/** /**
* An ISO 639-1 compliant language code (two letters in lower case). * 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. * Whether the source has support for latest updates.

View File

@@ -18,8 +18,10 @@ interface ConfigurableSource : Source {
fun setupPreferenceScreen(screen: PreferenceScreen) 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 // TODO: use getSourcePreferences once all extensions are on ext-lib 1.5
fun ConfigurableSource.sourcePreferences(): SharedPreferences = fun ConfigurableSource.sourcePreferences(): SharedPreferences =
Injekt.get<Application>().getSharedPreferences(preferenceKey(), Context.MODE_PRIVATE) Injekt.get<Application>().getSharedPreferences(preferenceKey(), Context.MODE_PRIVATE)
fun sourcePreferences(key: String): SharedPreferences = Injekt.get<Application>().getSharedPreferences(key, Context.MODE_PRIVATE)

View File

@@ -7,11 +7,11 @@ import rx.Observable
import suwayomi.tachidesk.manga.impl.util.lang.awaitSingle 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 { interface Source {
/** /**
* Id for the source. Must be unique. * ID for the source. Must be unique.
*/ */
val id: Long val id: Long
@@ -20,6 +20,9 @@ interface Source {
*/ */
val name: String val name: String
val lang: String
get() = ""
/** /**
* Get the updated details for a manga. * Get the updated details for a manga.
* *
@@ -73,9 +76,7 @@ interface Source {
"Use the non-RxJava API instead", "Use the non-RxJava API instead",
ReplaceWith("getPageList"), ReplaceWith("getPageList"),
) )
fun fetchPageList(chapter: SChapter): Observable<List<Page>> = Observable.empty() fun fetchPageList(chapter: SChapter): Observable<List<Page>> = throw IllegalStateException("Not used")
} }
// fun Source.icon(): Drawable? = Injekt.get<ExtensionManager>().getAppIconForSource(this) // fun Source.icon(): Drawable? = Injekt.get<ExtensionManager>().getAppIconForSource(this)
fun Source.getPreferenceKey(): String = "source_$id"

View File

@@ -32,6 +32,7 @@ import kotlinx.coroutines.withContext
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.json.decodeFromStream import kotlinx.serialization.json.decodeFromStream
import mu.KotlinLogging import mu.KotlinLogging
import nl.adaptivity.xmlutil.ExperimentalXmlUtilApi
import nl.adaptivity.xmlutil.core.KtXmlReader import nl.adaptivity.xmlutil.core.KtXmlReader
import nl.adaptivity.xmlutil.serialization.XML import nl.adaptivity.xmlutil.serialization.XML
import org.apache.commons.compress.archivers.zip.ZipFile import org.apache.commons.compress.archivers.zip.ZipFile
@@ -271,6 +272,7 @@ class LocalSource(
} }
} }
@OptIn(ExperimentalXmlUtilApi::class)
private fun setMangaDetailsFromComicInfoFile( private fun setMangaDetailsFromComicInfoFile(
stream: InputStream, stream: InputStream,
manga: SManga, manga: SManga,

View File

@@ -26,18 +26,12 @@ import java.security.MessageDigest
/** /**
* A simple implementation for sources from a website. * A simple implementation for sources from a website.
*/ */
@Suppress("unused")
abstract class HttpSource : CatalogueSource { abstract class HttpSource : CatalogueSource {
/** /**
* Network service. * Network service.
*/ */
val network: NetworkHelper by injectLazy() protected val network: NetworkHelper by injectLazy()
// /**
// * Preferences that a source may need.
// */
// val preferences: SharedPreferences by lazy {
// Injekt.get<Application>().getSharedPreferences(source.getPreferenceKey(), Context.MODE_PRIVATE)
// }
/** /**
* Base url of the website without the trailing slash, like: http://mysite.com * 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`. * 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. * Headers used for requests.
@@ -73,6 +67,10 @@ abstract class HttpSource : CatalogueSource {
open val client: OkHttpClient open val client: OkHttpClient
get() = network.client 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 * 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 * [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 * @param versionId [Int] the version ID of the source
* @return a unique ID for the source * @return a unique ID for the source
*/ */
@Suppress("MemberVisibilityCanBePrivate")
protected fun generateId( protected fun generateId(
name: String, name: String,
lang: String, lang: String,
@@ -155,8 +154,15 @@ abstract class HttpSource : CatalogueSource {
query: String, query: String,
filters: FilterList, filters: FilterList,
): Observable<MangasPage> { ): Observable<MangasPage> {
return client.newCall(searchMangaRequest(page, query, filters)) return Observable.defer {
.asObservableSuccess() 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 -> .map { response ->
searchMangaParse(response) searchMangaParse(response)
} }
@@ -387,7 +393,7 @@ abstract class HttpSource : CatalogueSource {
* *
* @param page the chapter whose page list has to be fetched * @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) return GET(page.imageUrl!!, headers)
} }
@@ -418,7 +424,7 @@ abstract class HttpSource : CatalogueSource {
*/ */
private fun getUrlWithoutDomain(orig: String): String { private fun getUrlWithoutDomain(orig: String): String {
return try { return try {
val uri = URI(orig) val uri = URI(orig.replace(" ", "%20"))
var out = uri.path var out = uri.path
if (uri.query != null) { if (uri.query != null) {
out += "?" + uri.query out += "?" + uri.query

View File

@@ -12,15 +12,8 @@ import graphql.schema.DataFetchingEnvironment
import org.jetbrains.exposed.sql.Column import org.jetbrains.exposed.sql.Column
import org.jetbrains.exposed.sql.Op import org.jetbrains.exposed.sql.Op
import org.jetbrains.exposed.sql.SortOrder 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.greater
import org.jetbrains.exposed.sql.SqlExpressionBuilder.less import org.jetbrains.exposed.sql.SqlExpressionBuilder.less
import org.jetbrains.exposed.sql.andWhere
import org.jetbrains.exposed.sql.selectAll import org.jetbrains.exposed.sql.selectAll
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction
import suwayomi.tachidesk.graphql.queries.filter.BooleanFilter 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.OrderBy
import suwayomi.tachidesk.graphql.server.primitives.PageInfo import suwayomi.tachidesk.graphql.server.primitives.PageInfo
import suwayomi.tachidesk.graphql.server.primitives.QueryResults 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.greaterNotUnique
import suwayomi.tachidesk.graphql.server.primitives.lessNotUnique import suwayomi.tachidesk.graphql.server.primitives.lessNotUnique
import suwayomi.tachidesk.graphql.server.primitives.maybeSwap import suwayomi.tachidesk.graphql.server.primitives.maybeSwap
@@ -157,21 +151,12 @@ class CategoryQuery {
val firstResult = res.firstOrNull()?.get(CategoryTable.id)?.value val firstResult = res.firstOrNull()?.get(CategoryTable.id)?.value
val lastResult = res.lastOrNull()?.get(CategoryTable.id)?.value val lastResult = res.lastOrNull()?.get(CategoryTable.id)?.value
if (after != null) { res.applyBeforeAfter(
res.andWhere { before = before,
when (orderByType) { after = after,
DESC, DESC_NULLS_FIRST, DESC_NULLS_LAST -> (orderBy ?: CategoryOrderBy.ID).less(after) orderBy = orderBy ?: CategoryOrderBy.ID,
null, ASC, ASC_NULLS_FIRST, ASC_NULLS_LAST -> (orderBy ?: CategoryOrderBy.ID).greater(after) orderByType = orderByType,
} )
}
} 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)
}
}
}
if (first != null) { if (first != null) {
res.limit(first, offset?.toLong() ?: 0) res.limit(first, offset?.toLong() ?: 0)

View File

@@ -12,18 +12,11 @@ import graphql.schema.DataFetchingEnvironment
import org.jetbrains.exposed.sql.Column import org.jetbrains.exposed.sql.Column
import org.jetbrains.exposed.sql.Op import org.jetbrains.exposed.sql.Op
import org.jetbrains.exposed.sql.SortOrder 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.greater
import org.jetbrains.exposed.sql.SqlExpressionBuilder.less import org.jetbrains.exposed.sql.SqlExpressionBuilder.less
import org.jetbrains.exposed.sql.andWhere import org.jetbrains.exposed.sql.andWhere
import org.jetbrains.exposed.sql.selectAll import org.jetbrains.exposed.sql.selectAll
import org.jetbrains.exposed.sql.transactions.transaction 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.BooleanFilter
import suwayomi.tachidesk.graphql.queries.filter.Filter import suwayomi.tachidesk.graphql.queries.filter.Filter
import suwayomi.tachidesk.graphql.queries.filter.FloatFilter 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.OrderBy
import suwayomi.tachidesk.graphql.server.primitives.PageInfo import suwayomi.tachidesk.graphql.server.primitives.PageInfo
import suwayomi.tachidesk.graphql.server.primitives.QueryResults 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.greaterNotUnique
import suwayomi.tachidesk.graphql.server.primitives.lessNotUnique import suwayomi.tachidesk.graphql.server.primitives.lessNotUnique
import suwayomi.tachidesk.graphql.server.primitives.maybeSwap import suwayomi.tachidesk.graphql.server.primitives.maybeSwap
@@ -241,21 +235,12 @@ class ChapterQuery {
val firstResult = res.firstOrNull()?.get(ChapterTable.id)?.value val firstResult = res.firstOrNull()?.get(ChapterTable.id)?.value
val lastResult = res.lastOrNull()?.get(ChapterTable.id)?.value val lastResult = res.lastOrNull()?.get(ChapterTable.id)?.value
if (after != null) { res.applyBeforeAfter(
res.andWhere { before = before,
when (orderByType) { after = after,
DESC, DESC_NULLS_FIRST, DESC_NULLS_LAST -> (orderBy ?: ID).less(after) orderBy = orderBy ?: ChapterOrderBy.ID,
null, ASC, ASC_NULLS_FIRST, ASC_NULLS_LAST -> (orderBy ?: ID).greater(after) orderByType = orderByType,
} )
}
} 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)
}
}
}
if (first != null) { if (first != null) {
res.limit(first, offset?.toLong() ?: 0) res.limit(first, offset?.toLong() ?: 0)

View File

@@ -13,16 +13,9 @@ import graphql.schema.DataFetchingEnvironment
import org.jetbrains.exposed.sql.Column import org.jetbrains.exposed.sql.Column
import org.jetbrains.exposed.sql.Op import org.jetbrains.exposed.sql.Op
import org.jetbrains.exposed.sql.SortOrder 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.greater
import org.jetbrains.exposed.sql.SqlExpressionBuilder.less import org.jetbrains.exposed.sql.SqlExpressionBuilder.less
import org.jetbrains.exposed.sql.SqlExpressionBuilder.neq import org.jetbrains.exposed.sql.SqlExpressionBuilder.neq
import org.jetbrains.exposed.sql.andWhere
import org.jetbrains.exposed.sql.selectAll import org.jetbrains.exposed.sql.selectAll
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction
import suwayomi.tachidesk.graphql.queries.filter.BooleanFilter 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.OrderBy
import suwayomi.tachidesk.graphql.server.primitives.PageInfo import suwayomi.tachidesk.graphql.server.primitives.PageInfo
import suwayomi.tachidesk.graphql.server.primitives.QueryResults 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.greaterNotUnique
import suwayomi.tachidesk.graphql.server.primitives.lessNotUnique import suwayomi.tachidesk.graphql.server.primitives.lessNotUnique
import suwayomi.tachidesk.graphql.server.primitives.maybeSwap import suwayomi.tachidesk.graphql.server.primitives.maybeSwap
@@ -187,21 +181,12 @@ class ExtensionQuery {
val firstResult = res.firstOrNull()?.get(ExtensionTable.pkgName) val firstResult = res.firstOrNull()?.get(ExtensionTable.pkgName)
val lastResult = res.lastOrNull()?.get(ExtensionTable.pkgName) val lastResult = res.lastOrNull()?.get(ExtensionTable.pkgName)
if (after != null) { res.applyBeforeAfter(
res.andWhere { before = before,
when (orderByType) { after = after,
DESC, DESC_NULLS_FIRST, DESC_NULLS_LAST -> (orderBy ?: ExtensionOrderBy.PKG_NAME).less(after) orderBy = orderBy ?: ExtensionOrderBy.PKG_NAME,
null, ASC, ASC_NULLS_FIRST, ASC_NULLS_LAST -> (orderBy ?: ExtensionOrderBy.PKG_NAME).greater(after) orderByType = orderByType,
} )
}
} 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)
}
}
}
if (first != null) { if (first != null) {
res.limit(first, offset?.toLong() ?: 0) res.limit(first, offset?.toLong() ?: 0)

View File

@@ -12,15 +12,8 @@ import graphql.schema.DataFetchingEnvironment
import org.jetbrains.exposed.sql.Column import org.jetbrains.exposed.sql.Column
import org.jetbrains.exposed.sql.Op import org.jetbrains.exposed.sql.Op
import org.jetbrains.exposed.sql.SortOrder 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.greater
import org.jetbrains.exposed.sql.SqlExpressionBuilder.less import org.jetbrains.exposed.sql.SqlExpressionBuilder.less
import org.jetbrains.exposed.sql.andWhere
import org.jetbrains.exposed.sql.selectAll import org.jetbrains.exposed.sql.selectAll
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction
import suwayomi.tachidesk.graphql.queries.filter.BooleanFilter 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.OrderBy
import suwayomi.tachidesk.graphql.server.primitives.PageInfo import suwayomi.tachidesk.graphql.server.primitives.PageInfo
import suwayomi.tachidesk.graphql.server.primitives.QueryResults 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.greaterNotUnique
import suwayomi.tachidesk.graphql.server.primitives.lessNotUnique import suwayomi.tachidesk.graphql.server.primitives.lessNotUnique
import suwayomi.tachidesk.graphql.server.primitives.maybeSwap import suwayomi.tachidesk.graphql.server.primitives.maybeSwap
@@ -245,21 +239,12 @@ class MangaQuery {
val firstResult = res.firstOrNull()?.get(MangaTable.id)?.value val firstResult = res.firstOrNull()?.get(MangaTable.id)?.value
val lastResult = res.lastOrNull()?.get(MangaTable.id)?.value val lastResult = res.lastOrNull()?.get(MangaTable.id)?.value
if (after != null) { res.applyBeforeAfter(
res.andWhere { before = before,
when (orderByType) { after = after,
DESC, DESC_NULLS_FIRST, DESC_NULLS_LAST -> (orderBy ?: MangaOrderBy.ID).less(after) orderBy = orderBy ?: MangaOrderBy.ID,
null, ASC, ASC_NULLS_FIRST, ASC_NULLS_LAST -> (orderBy ?: MangaOrderBy.ID).greater(after) orderByType = orderByType,
} )
}
} 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)
}
}
}
if (first != null) { if (first != null) {
res.limit(first, offset?.toLong() ?: 0) res.limit(first, offset?.toLong() ?: 0)

View File

@@ -12,15 +12,8 @@ import graphql.schema.DataFetchingEnvironment
import org.jetbrains.exposed.sql.Column import org.jetbrains.exposed.sql.Column
import org.jetbrains.exposed.sql.Op import org.jetbrains.exposed.sql.Op
import org.jetbrains.exposed.sql.SortOrder 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.greater
import org.jetbrains.exposed.sql.SqlExpressionBuilder.less import org.jetbrains.exposed.sql.SqlExpressionBuilder.less
import org.jetbrains.exposed.sql.andWhere
import org.jetbrains.exposed.sql.selectAll import org.jetbrains.exposed.sql.selectAll
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction
import suwayomi.tachidesk.global.model.table.GlobalMetaTable 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.OrderBy
import suwayomi.tachidesk.graphql.server.primitives.PageInfo import suwayomi.tachidesk.graphql.server.primitives.PageInfo
import suwayomi.tachidesk.graphql.server.primitives.QueryResults 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.greaterNotUnique
import suwayomi.tachidesk.graphql.server.primitives.lessNotUnique import suwayomi.tachidesk.graphql.server.primitives.lessNotUnique
import suwayomi.tachidesk.graphql.server.primitives.maybeSwap import suwayomi.tachidesk.graphql.server.primitives.maybeSwap
@@ -141,21 +135,12 @@ class MetaQuery {
val firstResult = res.firstOrNull()?.get(GlobalMetaTable.key) val firstResult = res.firstOrNull()?.get(GlobalMetaTable.key)
val lastResult = res.lastOrNull()?.get(GlobalMetaTable.key) val lastResult = res.lastOrNull()?.get(GlobalMetaTable.key)
if (after != null) { res.applyBeforeAfter(
res.andWhere { before = before,
when (orderByType) { after = after,
DESC, DESC_NULLS_FIRST, DESC_NULLS_LAST -> (orderBy ?: MetaOrderBy.KEY).less(after) orderBy = orderBy ?: MetaOrderBy.KEY,
null, ASC, ASC_NULLS_FIRST, ASC_NULLS_LAST -> (orderBy ?: MetaOrderBy.KEY).greater(after) orderByType = orderByType,
} )
}
} 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)
}
}
}
if (first != null) { if (first != null) {
res.limit(first, offset?.toLong() ?: 0) res.limit(first, offset?.toLong() ?: 0)

View File

@@ -12,15 +12,8 @@ import graphql.schema.DataFetchingEnvironment
import org.jetbrains.exposed.sql.Column import org.jetbrains.exposed.sql.Column
import org.jetbrains.exposed.sql.Op import org.jetbrains.exposed.sql.Op
import org.jetbrains.exposed.sql.SortOrder 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.greater
import org.jetbrains.exposed.sql.SqlExpressionBuilder.less import org.jetbrains.exposed.sql.SqlExpressionBuilder.less
import org.jetbrains.exposed.sql.andWhere
import org.jetbrains.exposed.sql.selectAll import org.jetbrains.exposed.sql.selectAll
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.transactions.transaction
import suwayomi.tachidesk.graphql.queries.filter.BooleanFilter 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.OrderBy
import suwayomi.tachidesk.graphql.server.primitives.PageInfo import suwayomi.tachidesk.graphql.server.primitives.PageInfo
import suwayomi.tachidesk.graphql.server.primitives.QueryResults 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.greaterNotUnique
import suwayomi.tachidesk.graphql.server.primitives.lessNotUnique import suwayomi.tachidesk.graphql.server.primitives.lessNotUnique
import suwayomi.tachidesk.graphql.server.primitives.maybeSwap import suwayomi.tachidesk.graphql.server.primitives.maybeSwap
@@ -157,21 +151,12 @@ class SourceQuery {
val firstResult = res.firstOrNull()?.get(SourceTable.id)?.value val firstResult = res.firstOrNull()?.get(SourceTable.id)?.value
val lastResult = res.lastOrNull()?.get(SourceTable.id)?.value val lastResult = res.lastOrNull()?.get(SourceTable.id)?.value
if (after != null) { res.applyBeforeAfter(
res.andWhere { before = before,
when (orderByType) { after = after,
DESC, DESC_NULLS_FIRST, DESC_NULLS_LAST -> (orderBy ?: SourceOrderBy.ID).less(after) orderBy = orderBy ?: SourceOrderBy.ID,
null, ASC, ASC_NULLS_FIRST, ASC_NULLS_LAST -> (orderBy ?: SourceOrderBy.ID).greater(after) orderByType = orderByType,
} )
}
} 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)
}
}
}
if (first != null) { if (first != null) {
res.limit(first, offset?.toLong() ?: 0) res.limit(first, offset?.toLong() ?: 0)

View File

@@ -313,7 +313,7 @@ data class StringListFilter(
val hasNoneInsensitive: List<String>? = null, val hasNoneInsensitive: List<String>? = null,
) : ListScalarFilter<String, List<String>> ) : ListScalarFilter<String, List<String>>
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST", "FINAL_UPPER_BOUND")
fun <T : String, S : T?> andFilterWithCompareString( fun <T : String, S : T?> andFilterWithCompareString(
column: Column<S>, column: Column<S>,
filter: StringFilter?, filter: StringFilter?,

View File

@@ -5,6 +5,8 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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, You can obtain one at https://mozilla.org/MPL/2.0/. */
@file:Suppress("DEPRECATION")
package suwayomi.tachidesk.graphql.server package suwayomi.tachidesk.graphql.server
import com.expediagroup.graphql.generator.execution.GraphQLContext import com.expediagroup.graphql.generator.execution.GraphQLContext
@@ -30,7 +32,9 @@ class TachideskGraphQLContextFactory : GraphQLContextFactory<GraphQLContext, Con
// } // }
// } // }
fun generateContextMap(request: WsContext): Map<*, Any> = emptyMap<Any, Any>() fun generateContextMap(
@Suppress("UNUSED_PARAMETER") request: WsContext,
): Map<*, Any> = emptyMap<Any, Any>()
} }
/** /**

View File

@@ -15,6 +15,7 @@ import graphql.GraphQL
import io.javalin.http.Context import io.javalin.http.Context
import io.javalin.websocket.WsCloseContext import io.javalin.websocket.WsCloseContext
import io.javalin.websocket.WsMessageContext import io.javalin.websocket.WsMessageContext
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
@@ -30,6 +31,7 @@ class TachideskGraphQLServer(
private val objectMapper = jacksonObjectMapper() private val objectMapper = jacksonObjectMapper()
private val subscriptionProtocolHandler = ApolloSubscriptionProtocolHandler(contextFactory, subscriptionHandler, objectMapper) private val subscriptionProtocolHandler = ApolloSubscriptionProtocolHandler(contextFactory, subscriptionHandler, objectMapper)
@OptIn(DelicateCoroutinesApi::class)
fun handleSubscriptionMessage(context: WsMessageContext) { fun handleSubscriptionMessage(context: WsMessageContext) {
subscriptionProtocolHandler.handleMessage(context) subscriptionProtocolHandler.handleMessage(context)
.map { objectMapper.writeValueAsString(it) } .map { objectMapper.writeValueAsString(it) }

View File

@@ -3,11 +3,13 @@ package suwayomi.tachidesk.graphql.server.primitives
import org.jetbrains.exposed.dao.id.EntityID import org.jetbrains.exposed.dao.id.EntityID
import org.jetbrains.exposed.sql.Column import org.jetbrains.exposed.sql.Column
import org.jetbrains.exposed.sql.Op import org.jetbrains.exposed.sql.Op
import org.jetbrains.exposed.sql.Query
import org.jetbrains.exposed.sql.SortOrder import org.jetbrains.exposed.sql.SortOrder
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
import org.jetbrains.exposed.sql.SqlExpressionBuilder.greater import org.jetbrains.exposed.sql.SqlExpressionBuilder.greater
import org.jetbrains.exposed.sql.SqlExpressionBuilder.less import org.jetbrains.exposed.sql.SqlExpressionBuilder.less
import org.jetbrains.exposed.sql.and import org.jetbrains.exposed.sql.and
import org.jetbrains.exposed.sql.andWhere
import org.jetbrains.exposed.sql.or import org.jetbrains.exposed.sql.or
interface OrderBy<T> { interface OrderBy<T> {
@@ -36,6 +38,29 @@ fun SortOrder?.maybeSwap(value: Any?): SortOrder {
} }
} }
fun <T> Query.applyBeforeAfter(
before: Cursor?,
after: Cursor?,
orderBy: OrderBy<T>,
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") @JvmName("greaterNotUniqueIntKey")
fun <T : Comparable<T>> greaterNotUnique( fun <T : Comparable<T>> greaterNotUnique(
column: Column<T>, column: Column<T>,

View File

@@ -38,8 +38,8 @@ import suwayomi.tachidesk.graphql.server.toGraphQLContext
import suwayomi.tachidesk.server.serverConfig import suwayomi.tachidesk.server.serverConfig
/** /**
* Implementation of the `graphql-ws` protocol defined by Apollo * Implementation of the `graphql-transport-ws` protocol defined by Denis Badurina
* https://github.com/apollographql/subscriptions-transport-ws/blob/master/PROTOCOL.md * https://github.com/enisdenjo/graphql-ws/blob/master/PROTOCOL.md
* ported for Javalin * ported for Javalin
*/ */
class ApolloSubscriptionProtocolHandler( class ApolloSubscriptionProtocolHandler(
@@ -47,6 +47,10 @@ class ApolloSubscriptionProtocolHandler(
private val subscriptionHandler: GraphQLSubscriptionHandler, private val subscriptionHandler: GraphQLSubscriptionHandler,
private val objectMapper: ObjectMapper, private val objectMapper: ObjectMapper,
) { ) {
companion object {
private const val UNKNOWN_OPERATION_NAME = "__UNKNOWN__"
}
private val sessionState = ApolloSubscriptionSessionState() private val sessionState = ApolloSubscriptionSessionState()
private val logger = KotlinLogging.logger {} private val logger = KotlinLogging.logger {}
private val pongMessage = SubscriptionOperationMessage(type = GQL_PONG.type) private val pongMessage = SubscriptionOperationMessage(type = GQL_PONG.type)
@@ -54,14 +58,10 @@ class ApolloSubscriptionProtocolHandler(
private val acknowledgeMessage = SubscriptionOperationMessage(GQL_CONNECTION_ACK.type) private val acknowledgeMessage = SubscriptionOperationMessage(GQL_CONNECTION_ACK.type)
private fun getOperationName(payload: Any?): String { private fun getOperationName(payload: Any?): String {
val unknownOperationName = "__UNKNOWN__" @Suppress("UNCHECKED_CAST")
return (payload as? Map<String, String>)
try { .orEmpty()
@Suppress("UNCHECKED_CAST") .getOrDefault("operationName", UNKNOWN_OPERATION_NAME)
return (payload as Map<String, String>)["operationName"] ?: unknownOperationName
} catch (e: Exception) {
return unknownOperationName
}
} }
fun handleMessage(context: WsMessageContext): Flow<SubscriptionOperationMessage> { fun handleMessage(context: WsMessageContext): Flow<SubscriptionOperationMessage> {
@@ -83,12 +83,12 @@ class ApolloSubscriptionProtocolHandler(
return try { return try {
when (operationMessage.type) { when (operationMessage.type) {
GQL_CONNECTION_INIT.type -> onInit(operationMessage, context) GQL_CONNECTION_INIT.type -> onInit(context)
GQL_SUBSCRIBE.type -> startSubscription(operationMessage, context) GQL_SUBSCRIBE.type -> startSubscription(operationMessage, context)
GQL_COMPLETE.type -> onComplete(operationMessage, context) GQL_COMPLETE.type -> onComplete(operationMessage)
GQL_PING.type -> onPing() GQL_PING.type -> onPing()
GQL_PONG.type -> emptyFlow() GQL_PONG.type -> emptyFlow()
else -> onUnknownOperation(operationMessage, context) else -> onUnknownOperation(operationMessage)
} }
} catch (exception: Exception) { } catch (exception: Exception) {
onException(exception) onException(exception)
@@ -108,7 +108,6 @@ class ApolloSubscriptionProtocolHandler(
} }
} }
@Suppress("Detekt.TooGenericExceptionCaught")
private fun startSubscription( private fun startSubscription(
operationMessage: SubscriptionOperationMessage, operationMessage: SubscriptionOperationMessage,
context: WsContext, context: WsContext,
@@ -142,7 +141,7 @@ class ApolloSubscriptionProtocolHandler(
SubscriptionOperationMessage(type = GQL_NEXT.type, id = operationMessage.id, payload = it) 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) } .onStart { sessionState.saveOperation(context, operationMessage, currentCoroutineContext().job) }
} catch (exception: Exception) { } catch (exception: Exception) {
logger.error("Error running graphql subscription", exception) logger.error("Error running graphql subscription", exception)
@@ -152,21 +151,15 @@ class ApolloSubscriptionProtocolHandler(
} }
} }
private fun onInit( private fun onInit(context: WsContext): Flow<SubscriptionOperationMessage> {
operationMessage: SubscriptionOperationMessage, saveContext(context)
context: WsContext,
): Flow<SubscriptionOperationMessage> {
saveContext(operationMessage, context)
return flowOf(acknowledgeMessage) return flowOf(acknowledgeMessage)
} }
/** /**
* Generate the context and save it for all future messages. * Generate the context and save it for all future messages.
*/ */
private fun saveContext( private fun saveContext(context: WsContext) {
operationMessage: SubscriptionOperationMessage,
context: WsContext,
) {
runBlocking { runBlocking {
val graphQLContext = contextFactory.generateContextMap(context).toGraphQLContext() val graphQLContext = contextFactory.generateContextMap(context).toGraphQLContext()
sessionState.saveContext(context, graphQLContext) sessionState.saveContext(context, graphQLContext)
@@ -176,10 +169,7 @@ class ApolloSubscriptionProtocolHandler(
/** /**
* Called with the publisher has completed on its own. * Called with the publisher has completed on its own.
*/ */
private fun onComplete( private fun onComplete(operationMessage: SubscriptionOperationMessage): Flow<SubscriptionOperationMessage> {
operationMessage: SubscriptionOperationMessage,
context: WsContext,
): Flow<SubscriptionOperationMessage> {
return sessionState.completeOperation(operationMessage) return sessionState.completeOperation(operationMessage)
} }
@@ -192,10 +182,7 @@ class ApolloSubscriptionProtocolHandler(
return emptyFlow() return emptyFlow()
} }
private fun onUnknownOperation( private fun onUnknownOperation(operationMessage: SubscriptionOperationMessage): Flow<SubscriptionOperationMessage> {
operationMessage: SubscriptionOperationMessage,
context: WsContext,
): Flow<SubscriptionOperationMessage> {
logger.error("Unknown subscription operation $operationMessage") logger.error("Unknown subscription operation $operationMessage")
sessionState.completeOperation(operationMessage) sessionState.completeOperation(operationMessage)
return emptyFlow() return emptyFlow()

View File

@@ -22,11 +22,4 @@ interface Chapter : SChapter, Serializable {
val isRecognizedNumber: Boolean val isRecognizedNumber: Boolean
get() = chapter_number >= 0f get() = chapter_number >= 0f
companion object {
fun create(): Chapter =
ChapterImpl().apply {
chapter_number = -1f
}
}
} }

View File

@@ -2,9 +2,6 @@
package suwayomi.tachidesk.manga.impl.backup.models package suwayomi.tachidesk.manga.impl.backup.models
import org.jetbrains.exposed.sql.ResultRow
import suwayomi.tachidesk.manga.model.table.ChapterTable
class ChapterImpl : Chapter { class ChapterImpl : Chapter {
override var id: Long? = null override var id: Long? = null
@@ -42,17 +39,4 @@ class ChapterImpl : Chapter {
override fun hashCode(): Int { override fun hashCode(): Int {
return url.hashCode() + id.hashCode() 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 <--
} }

View File

@@ -120,22 +120,6 @@ interface Manga : SManga {
const val CHAPTER_DISPLAY_NAME = 0x00000000 const val CHAPTER_DISPLAY_NAME = 0x00000000
const val CHAPTER_DISPLAY_NUMBER = 0x00100000 const val CHAPTER_DISPLAY_NUMBER = 0x00100000
const val CHAPTER_DISPLAY_MASK = 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
}
} }
} }

View File

@@ -3,8 +3,6 @@
package suwayomi.tachidesk.manga.impl.backup.models package suwayomi.tachidesk.manga.impl.backup.models
import eu.kanade.tachiyomi.source.model.UpdateStrategy import eu.kanade.tachiyomi.source.model.UpdateStrategy
import org.jetbrains.exposed.sql.ResultRow
import suwayomi.tachidesk.manga.model.table.MangaTable
open class MangaImpl : Manga { open class MangaImpl : Manga {
override var id: Long? = null override var id: Long? = null
@@ -68,18 +66,4 @@ open class MangaImpl : Manga {
override fun hashCode(): Int { override fun hashCode(): Int {
return url.hashCode() + id.hashCode() 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 <--
} }

View File

@@ -30,19 +30,4 @@ interface Track : Serializable {
var finished_reading_date: Long var finished_reading_date: Long
var tracking_url: String 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
}
}
} }

View File

@@ -2,7 +2,6 @@ package suwayomi.tachidesk.manga.impl.backup.proto.models
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoNumber import kotlinx.serialization.protobuf.ProtoNumber
import suwayomi.tachidesk.manga.impl.backup.models.Chapter
import suwayomi.tachidesk.manga.impl.backup.models.ChapterImpl import suwayomi.tachidesk.manga.impl.backup.models.ChapterImpl
@Serializable @Serializable
@@ -36,21 +35,4 @@ data class BackupChapter(
source_order = this@BackupChapter.sourceOrder 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,
)
}
}
} }

View File

@@ -13,6 +13,7 @@ import io.javalin.websocket.WsContext
import io.javalin.websocket.WsMessageContext import io.javalin.websocket.WsMessageContext
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll import kotlinx.coroutines.awaitAll
@@ -51,6 +52,7 @@ import kotlin.time.Duration.Companion.seconds
private val logger = KotlinLogging.logger {} private val logger = KotlinLogging.logger {}
@OptIn(FlowPreview::class)
object DownloadManager { object DownloadManager {
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Default) private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
private val clients = ConcurrentHashMap<String, WsContext>() private val clients = ConcurrentHashMap<String, WsContext>()
@@ -150,9 +152,6 @@ object DownloadManager {
if (immediate) { if (immediate) {
sendStatusToAllClients() sendStatusToAllClients()
} }
/*if (downloadChapter != null) { TODO GRAPHQL
downloadSubscriptionSource.publish(downloadChapter)
}*/
} }
private fun getStatus(): DownloadStatus { private fun getStatus(): DownloadStatus {

View File

@@ -12,6 +12,7 @@ fun interface FileDownload0Args : FileDownload {
} }
} }
@Suppress("UNCHECKED_CAST")
fun interface FileDownload3Args<A, B, C> : FileDownload { fun interface FileDownload3Args<A, B, C> : FileDownload {
suspend fun execute( suspend fun execute(
a: A, a: A,

View File

@@ -14,6 +14,7 @@ fun interface RetrieveFile0Args : RetrieveFile {
} }
} }
@Suppress("UNCHECKED_CAST")
fun interface RetrieveFile1Args<A> : RetrieveFile { fun interface RetrieveFile1Args<A> : RetrieveFile {
fun execute(a: A): Pair<InputStream, String> fun execute(a: A): Pair<InputStream, String>

View File

@@ -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 * 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, You can obtain one at https://mozilla.org/MPL/2.0/. */
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.coroutines.suspendCancellableCoroutine
import okhttp3.Call import okhttp3.Call
import okhttp3.Callback import okhttp3.Callback
@@ -16,6 +17,7 @@ import java.io.IOException
import kotlin.coroutines.resumeWithException import kotlin.coroutines.resumeWithException
// Based on https://github.com/gildor/kotlin-coroutines-okhttp // Based on https://github.com/gildor/kotlin-coroutines-okhttp
@OptIn(ExperimentalCoroutinesApi::class)
suspend fun Call.await(): Response { suspend fun Call.await(): Response {
return suspendCancellableCoroutine { continuation -> return suspendCancellableCoroutine { continuation ->
enqueue( enqueue(

View File

@@ -10,6 +10,7 @@ package suwayomi.tachidesk.server
import com.typesafe.config.Config import com.typesafe.config.Config
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
@@ -111,6 +112,7 @@ class ServerConfig(getConfig: () -> Config, val moduleName: String = SERVER_CONF
// local source // local source
val localSourcePath: MutableStateFlow<String> by OverrideConfigValue(StringConfigAdapter) val localSourcePath: MutableStateFlow<String> by OverrideConfigValue(StringConfigAdapter)
@OptIn(ExperimentalCoroutinesApi::class)
fun <T> subscribeTo( fun <T> subscribeTo(
flow: Flow<T>, flow: Flow<T>,
onChange: suspend (value: T) -> Unit, onChange: suspend (value: T) -> Unit,

View File

@@ -12,6 +12,7 @@ import eu.kanade.tachiyomi.network.NetworkHelper
import eu.kanade.tachiyomi.network.awaitSuccess import eu.kanade.tachiyomi.network.awaitSuccess
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.channels.BufferOverflow.DROP_OLDEST import kotlinx.coroutines.channels.BufferOverflow.DROP_OLDEST
import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableSharedFlow
@@ -131,6 +132,8 @@ object WebInterfaceManager {
private val notifyFlow = private val notifyFlow =
MutableSharedFlow<WebUIUpdateStatus>(extraBufferCapacity = 1, onBufferOverflow = DROP_OLDEST) MutableSharedFlow<WebUIUpdateStatus>(extraBufferCapacity = 1, onBufferOverflow = DROP_OLDEST)
@OptIn(FlowPreview::class)
val status = val status =
notifyFlow.sample(1.seconds) notifyFlow.sample(1.seconds)
.stateIn( .stateIn(