[skip ci] Formatting

This commit is contained in:
Syer10
2024-09-03 21:37:18 -04:00
parent e968a2195a
commit 6c1fbfa63b
220 changed files with 2493 additions and 2519 deletions
@@ -32,7 +32,9 @@ import uy.kohesive.injekt.api.addSingleton
import uy.kohesive.injekt.api.addSingletonFactory
import uy.kohesive.injekt.api.get
class AppModule(val app: Application) : InjektModule {
class AppModule(
val app: Application,
) : InjektModule {
override fun InjektRegistrar.registerInjectables() {
addSingleton(app)
@@ -49,7 +49,9 @@ class MemoryCookieJar : CookieJar {
}
}
class WrappedCookie private constructor(val cookie: Cookie) {
class WrappedCookie private constructor(
val cookie: Cookie,
) {
fun unwrap() = cookie
fun isExpired() = cookie.expiresAt < System.currentTimeMillis()
@@ -30,7 +30,9 @@ import java.net.CookieManager
import java.net.CookiePolicy
import java.util.concurrent.TimeUnit
class NetworkHelper(context: Context) {
class NetworkHelper(
context: Context,
) {
// private val preferences: PreferencesHelper by injectLazy()
// private val cacheDir = File(context.cacheDir, "network_cache")
@@ -53,9 +55,7 @@ class NetworkHelper(context: Context) {
"AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
)
fun defaultUserAgentProvider(): String {
return userAgent.value
}
fun defaultUserAgentProvider(): String = userAgent.value
init {
@OptIn(DelicateCoroutinesApi::class)
@@ -63,14 +63,14 @@ class NetworkHelper(context: Context) {
.drop(1)
.onEach {
GetCatalogueSource.unregisterAllCatalogueSources() // need to reset the headers
}
.launchIn(GlobalScope)
}.launchIn(GlobalScope)
}
private val baseClientBuilder: OkHttpClient.Builder
get() {
val builder =
OkHttpClient.Builder()
OkHttpClient
.Builder()
.cookieJar(PersistentCookieJar(cookieStore))
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
@@ -80,8 +80,7 @@ class NetworkHelper(context: Context) {
directory = File.createTempFile("tachidesk_network_cache", null),
maxSize = 5L * 1024 * 1024, // 5 MiB
),
)
.addInterceptor(UncaughtExceptionInterceptor())
).addInterceptor(UncaughtExceptionInterceptor())
.addInterceptor(UserAgentInterceptor(::defaultUserAgentProvider))
.addNetworkInterceptor(IgnoreGzipInterceptor())
.addNetworkInterceptor(BrotliInterceptor)
@@ -50,9 +50,7 @@ fun Call.asObservable(): Observable<Response> {
// call.cancel()
}
override fun isUnsubscribed(): Boolean {
return call.isCanceled()
}
override fun isUnsubscribed(): Boolean = call.isCanceled()
}
subscriber.add(requestArbiter)
@@ -60,15 +58,14 @@ fun Call.asObservable(): Observable<Response> {
}
}
fun Call.asObservableSuccess(): Observable<Response> {
return asObservable()
fun Call.asObservableSuccess(): Observable<Response> =
asObservable()
.doOnNext { response ->
if (!response.isSuccessful) {
response.close()
throw HttpException(response.code)
}
}
}
// Based on https://github.com/gildor/kotlin-coroutines-okhttp
@OptIn(ExperimentalCoroutinesApi::class)
@@ -135,29 +132,28 @@ fun OkHttpClient.newCachelessCallWithProgress(
.cache(null)
.addNetworkInterceptor { chain ->
val originalResponse = chain.proceed(chain.request())
originalResponse.newBuilder()
originalResponse
.newBuilder()
.body(ProgressResponseBody(originalResponse.body, listener))
.build()
}
.build()
}.build()
return progressClient.newCall(request)
}
context(Json)
inline fun <reified T> Response.parseAs(): T {
return decodeFromJsonResponse(serializer(), this)
}
inline fun <reified T> Response.parseAs(): T = decodeFromJsonResponse(serializer(), this)
context(Json)
@OptIn(ExperimentalSerializationApi::class)
fun <T> decodeFromJsonResponse(
deserializer: DeserializationStrategy<T>,
response: Response,
): T {
return response.body.source().use {
): T =
response.body.source().use {
decodeFromBufferedSource(deserializer, it)
}
}
class HttpException(val code: Int) : IllegalStateException("HTTP error $code")
class HttpException(
val code: Int,
) : IllegalStateException("HTTP error $code")
@@ -5,7 +5,9 @@ import okhttp3.CookieJar
import okhttp3.HttpUrl
// from TachiWeb-Server
class PersistentCookieJar(private val store: PersistentCookieStore) : CookieJar {
class PersistentCookieJar(
private val store: PersistentCookieStore,
) : CookieJar {
override fun saveFromResponse(
url: HttpUrl,
cookies: List<Cookie>,
@@ -13,7 +15,5 @@ class PersistentCookieJar(private val store: PersistentCookieStore) : CookieJar
store.addAll(url, cookies)
}
override fun loadForRequest(url: HttpUrl): List<Cookie> {
return store.get(url)
}
override fun loadForRequest(url: HttpUrl): List<Cookie> = store.get(url)
}
@@ -15,7 +15,9 @@ import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.seconds
// from TachiWeb-Server
class PersistentCookieStore(context: Context) : CookieStore {
class PersistentCookieStore(
context: Context,
) : CookieStore {
private val cookieMap = ConcurrentHashMap<String, List<Cookie>>()
private val prefs = context.getSharedPreferences("cookie_store", Context.MODE_PRIVATE)
@@ -23,7 +25,8 @@ class PersistentCookieStore(context: Context) : CookieStore {
init {
val domains =
prefs.all.keys.map { it.substringBeforeLast(".") }
prefs.all.keys
.map { it.substringBeforeLast(".") }
.toSet()
domains.forEach { domain ->
val cookies = prefs.getStringSet(domain, emptySet())
@@ -31,7 +34,8 @@ class PersistentCookieStore(context: Context) : CookieStore {
try {
val url = "http://$domain".toHttpUrlOrNull() ?: return@forEach
val nonExpiredCookies =
cookies.mapNotNull { Cookie.parse(url, it) }
cookies
.mapNotNull { Cookie.parse(url, it) }
.filter { !it.hasExpired() }
cookieMap[domain] = nonExpiredCookies
} catch (e: Exception) {
@@ -63,14 +67,13 @@ class PersistentCookieStore(context: Context) : CookieStore {
}
}
override fun removeAll(): Boolean {
return lock.withLock {
override fun removeAll(): Boolean =
lock.withLock {
val wasNotEmpty = cookieMap.isEmpty()
prefs.edit().clear().apply()
cookieMap.clear()
wasNotEmpty
}
}
fun remove(uri: URI) {
val url = uri.toURL()
@@ -87,9 +90,7 @@ class PersistentCookieStore(context: Context) : CookieStore {
}
}
fun get(url: HttpUrl): List<Cookie> {
return get(url.host)
}
fun get(url: HttpUrl): List<Cookie> = get(url.host)
override fun add(
uri: URI?,
@@ -105,19 +106,17 @@ class PersistentCookieStore(context: Context) : CookieStore {
}
}
override fun getCookies(): List<HttpCookie> {
return cookieMap.values.flatMap {
override fun getCookies(): List<HttpCookie> =
cookieMap.values.flatMap {
it.map {
it.toHttpCookie()
}
}
}
override fun getURIs(): List<URI> {
return cookieMap.keys().toList().map {
override fun getURIs(): List<URI> =
cookieMap.keys().toList().map {
URI("http://$it")
}
}
override fun remove(
uri: URI?,
@@ -145,9 +144,7 @@ class PersistentCookieStore(context: Context) : CookieStore {
}
}
private fun get(url: String): List<Cookie> {
return cookieMap[url].orEmpty().filter { !it.hasExpired() }
}
private fun get(url: String): List<Cookie> = cookieMap[url].orEmpty().filter { !it.hasExpired() }
private fun saveToDisk(url: URL) {
// Get cookies to be stored in disk
@@ -165,7 +162,8 @@ class PersistentCookieStore(context: Context) : CookieStore {
private fun Cookie.hasExpired() = System.currentTimeMillis() >= expiresAt
private fun HttpCookie.toCookie(uri: URI) =
Cookie.Builder()
Cookie
.Builder()
.name(name)
.value(value)
.domain(uri.toURL().host)
@@ -176,22 +174,19 @@ class PersistentCookieStore(context: Context) : CookieStore {
} else {
it.expiresAt(Long.MAX_VALUE)
}
}
.let {
}.let {
if (secure) {
it.secure()
} else {
it
}
}
.let {
}.let {
if (isHttpOnly) {
it.httpOnly()
} else {
it
}
}
.build()
}.build()
private fun Cookie.toHttpCookie(): HttpCookie {
val it = this
@@ -9,22 +9,19 @@ import okio.Source
import okio.buffer
import java.io.IOException
class ProgressResponseBody(private val responseBody: ResponseBody, private val progressListener: ProgressListener) : ResponseBody() {
class ProgressResponseBody(
private val responseBody: ResponseBody,
private val progressListener: ProgressListener,
) : ResponseBody() {
private val bufferedSource: BufferedSource by lazy {
source(responseBody.source()).buffer()
}
override fun contentType(): MediaType? {
return responseBody.contentType()
}
override fun contentType(): MediaType? = responseBody.contentType()
override fun contentLength(): Long {
return responseBody.contentLength()
}
override fun contentLength(): Long = responseBody.contentLength()
override fun source(): BufferedSource {
return bufferedSource
}
override fun source(): BufferedSource = bufferedSource
private fun source(source: Source): Source {
return object : ForwardingSource(source) {
@@ -18,13 +18,13 @@ fun GET(
url: String,
headers: Headers = DEFAULT_HEADERS,
cache: CacheControl = DEFAULT_CACHE_CONTROL,
): Request {
return Request.Builder()
): Request =
Request
.Builder()
.url(url)
.headers(headers)
.cacheControl(cache)
.build()
}
/**
* @since extensions-lib 1.4
@@ -33,52 +33,52 @@ fun GET(
url: HttpUrl,
headers: Headers = DEFAULT_HEADERS,
cache: CacheControl = DEFAULT_CACHE_CONTROL,
): Request {
return Request.Builder()
): Request =
Request
.Builder()
.url(url)
.headers(headers)
.cacheControl(cache)
.build()
}
fun POST(
url: String,
headers: Headers = DEFAULT_HEADERS,
body: RequestBody = DEFAULT_BODY,
cache: CacheControl = DEFAULT_CACHE_CONTROL,
): Request {
return Request.Builder()
): Request =
Request
.Builder()
.url(url)
.post(body)
.headers(headers)
.cacheControl(cache)
.build()
}
fun PUT(
url: String,
headers: Headers = DEFAULT_HEADERS,
body: RequestBody = DEFAULT_BODY,
cache: CacheControl = DEFAULT_CACHE_CONTROL,
): Request {
return Request.Builder()
): Request =
Request
.Builder()
.url(url)
.put(body)
.headers(headers)
.cacheControl(cache)
.build()
}
fun DELETE(
url: String,
headers: Headers = DEFAULT_HEADERS,
body: RequestBody = DEFAULT_BODY,
cache: CacheControl = DEFAULT_CACHE_CONTROL,
): Request {
return Request.Builder()
): Request =
Request
.Builder()
.url(url)
.delete(body)
.headers(headers)
.cacheControl(cache)
.build()
}
@@ -117,12 +117,12 @@ object CFClearance {
serverConfig.flareSolverrTimeout
.map { timeoutInt ->
val timeout = timeoutInt.seconds
network.client.newBuilder()
network.client
.newBuilder()
.callTimeout(timeout.plus(10.seconds).toJavaDuration())
.readTimeout(timeout.plus(5.seconds).toJavaDuration())
.build()
}
.stateIn(GlobalScope, SharingStarted.Eagerly, network.client)
}.stateIn(GlobalScope, SharingStarted.Eagerly, network.client)
}
private val json: Json by injectLazy()
private val jsonMediaType = "application/json".toMediaType()
@@ -190,26 +190,29 @@ object CFClearance {
return with(json) {
mutex.withLock {
client.value.newCall(
POST(
url = serverConfig.flareSolverrUrl.value.removeSuffix("/") + "/v1",
body =
Json.encodeToString(
FlareSolverRequest(
"request.get",
originalRequest.url.toString(),
session = serverConfig.flareSolverrSessionName.value,
sessionTtlMinutes = serverConfig.flareSolverrSessionTtl.value,
cookies =
network.cookieStore.get(originalRequest.url).map {
FlareSolverCookie(it.name, it.value)
},
returnOnlyCookies = onlyCookies,
maxTimeout = timeout.inWholeMilliseconds.toInt(),
),
).toRequestBody(jsonMediaType),
),
).awaitSuccess().parseAs<FlareSolverResponse>()
client.value
.newCall(
POST(
url = serverConfig.flareSolverrUrl.value.removeSuffix("/") + "/v1",
body =
Json
.encodeToString(
FlareSolverRequest(
"request.get",
originalRequest.url.toString(),
session = serverConfig.flareSolverrSessionName.value,
sessionTtlMinutes = serverConfig.flareSolverrSessionTtl.value,
cookies =
network.cookieStore.get(originalRequest.url).map {
FlareSolverCookie(it.name, it.value)
},
returnOnlyCookies = onlyCookies,
maxTimeout = timeout.inWholeMilliseconds.toInt(),
),
).toRequestBody(jsonMediaType),
),
).awaitSuccess()
.parseAs<FlareSolverResponse>()
}
}
}
@@ -224,7 +227,8 @@ object CFClearance {
val cookies =
flareSolverResponse.solution.cookies
.map { cookie ->
Cookie.Builder()
Cookie
.Builder()
.name(cookie.name)
.value(cookie.value)
.domain(cookie.domain.removePrefix("."))
@@ -233,13 +237,12 @@ object CFClearance {
if (cookie.httpOnly != null && cookie.httpOnly) it.httpOnly()
if (cookie.secure != null && cookie.secure) it.secure()
if (!cookie.path.isNullOrEmpty()) it.path(cookie.path)
}
.build()
}
.groupBy { it.domain }
}.build()
}.groupBy { it.domain }
.flatMap { (domain, cookies) ->
network.cookieStore.addAll(
HttpUrl.Builder()
HttpUrl
.Builder()
.scheme("http")
.host(domain.removePrefix("."))
.build(),
@@ -254,7 +257,8 @@ object CFClearance {
"${it.name}=${it.value}"
}
logger.trace { "Final cookies\n$finalCookies" }
return originalRequest.newBuilder()
return originalRequest
.newBuilder()
.header("Cookie", finalCookies)
.header("User-Agent", flareSolverResponse.solution.userAgent)
.build()
@@ -13,8 +13,8 @@ import java.io.IOException
* See https://square.github.io/okhttp/4.x/okhttp/okhttp3/-interceptor/
*/
class UncaughtExceptionInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
return try {
override fun intercept(chain: Interceptor.Chain): Response =
try {
chain.proceed(chain.request())
} catch (e: Exception) {
if (e is IOException) {
@@ -23,5 +23,4 @@ class UncaughtExceptionInterceptor : Interceptor {
throw IOException(e)
}
}
}
}
@@ -3,7 +3,9 @@ package eu.kanade.tachiyomi.network.interceptor
import okhttp3.Interceptor
import okhttp3.Response
class UserAgentInterceptor(private val userAgentProvider: () -> String) : Interceptor {
class UserAgentInterceptor(
private val userAgentProvider: () -> String,
) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val originalRequest = chain.request()
@@ -23,9 +23,7 @@ interface CatalogueSource : Source {
* @param page the page number to retrieve.
*/
@Suppress("DEPRECATION")
suspend fun getPopularManga(page: Int): MangasPage {
return fetchPopularManga(page).awaitSingle()
}
suspend fun getPopularManga(page: Int): MangasPage = fetchPopularManga(page).awaitSingle()
/**
* Get a page with a list of manga.
@@ -40,9 +38,7 @@ interface CatalogueSource : Source {
page: Int,
query: String,
filters: FilterList,
): MangasPage {
return fetchSearchManga(page, query, filters).awaitSingle()
}
): MangasPage = fetchSearchManga(page, query, filters).awaitSingle()
/**
* Get a page with a list of latest manga updates.
@@ -51,9 +47,7 @@ interface CatalogueSource : Source {
* @param page the page number to retrieve.
*/
@Suppress("DEPRECATION")
suspend fun getLatestUpdates(page: Int): MangasPage {
return fetchLatestUpdates(page).awaitSingle()
}
suspend fun getLatestUpdates(page: Int): MangasPage = fetchLatestUpdates(page).awaitSingle()
/**
* Returns the list of filters for the source.
@@ -31,9 +31,7 @@ interface Source {
* @return the updated manga.
*/
@Suppress("DEPRECATION")
suspend fun getMangaDetails(manga: SManga): SManga {
return fetchMangaDetails(manga).awaitSingle()
}
suspend fun getMangaDetails(manga: SManga): SManga = fetchMangaDetails(manga).awaitSingle()
/**
* Get all the available chapters for a manga.
@@ -43,9 +41,7 @@ interface Source {
* @return the chapters for the manga.
*/
@Suppress("DEPRECATION")
suspend fun getChapterList(manga: SManga): List<SChapter> {
return fetchChapterList(manga).awaitSingle()
}
suspend fun getChapterList(manga: SManga): List<SChapter> = fetchChapterList(manga).awaitSingle()
/**
* Get the list of pages a chapter has. Pages should be returned
@@ -56,9 +52,7 @@ interface Source {
* @return the pages for the chapter.
*/
@Suppress("DEPRECATION")
suspend fun getPageList(chapter: SChapter): List<Page> {
return fetchPageList(chapter).awaitSingle()
}
suspend fun getPageList(chapter: SChapter): List<Page> = fetchPageList(chapter).awaitSingle()
@Deprecated(
"Use the non-RxJava API instead",
@@ -59,7 +59,8 @@ import com.github.junrar.Archive as JunrarArchive
class LocalSource(
private val fileSystem: LocalSourceFileSystem,
private val coverManager: LocalCoverManager,
) : CatalogueSource, UnmeteredSource {
) : CatalogueSource,
UnmeteredSource {
private val json: Json by injectLazy()
private val xml: XML by injectLazy()
@@ -93,7 +94,8 @@ class LocalSource(
// Filter out files that are hidden and is not a folder
.filter { it.isDirectory && !it.name.startsWith('.') }
.distinctBy { it.name }
.filter { // Filter by query or last modified
.filter {
// Filter by query or last modified
if (lastModifiedLimit == 0L) {
it.name.contains(query, ignoreCase = true)
} else {
@@ -134,7 +136,8 @@ class LocalSource(
url = mangaDir.name
// Try to find the cover
coverManager.find(mangaDir.name)
coverManager
.find(mangaDir.name)
?.takeIf(File::exists)
?.let { thumbnail_url = it.absolutePath }
}
@@ -238,7 +241,7 @@ class LocalSource(
for (chapter in chapterArchives) {
when (Format.valueOf(chapter)) {
is Format.Zip -> {
ZipFile(chapter).use { zip: ZipFile ->
ZipFile.builder().setFile(chapter).get().use { zip: ZipFile ->
zip.getEntry(COMIC_INFO_FILE)?.let { comicInfoFile ->
zip.getInputStream(comicInfoFile).buffered().use { stream ->
return copyComicInfoFile(stream, folderPath)
@@ -264,13 +267,12 @@ class LocalSource(
private fun copyComicInfoFile(
comicInfoFileStream: InputStream,
folderPath: String?,
): File {
return File("$folderPath/$COMIC_INFO_FILE").apply {
): File =
File("$folderPath/$COMIC_INFO_FILE").apply {
outputStream().use { outputStream ->
comicInfoFileStream.use { it.copyTo(outputStream) }
}
}
}
@OptIn(ExperimentalXmlUtilApi::class)
private fun setMangaDetailsFromComicInfoFile(
@@ -286,8 +288,9 @@ class LocalSource(
}
// Chapters
override suspend fun getChapterList(manga: SManga): List<SChapter> {
return fileSystem.getFilesInMangaDirectory(manga.url)
override suspend fun getChapterList(manga: SManga): List<SChapter> =
fileSystem
.getFilesInMangaDirectory(manga.url)
// Only keep supported formats
.filter { it.isDirectory || Archive.isSupported(it) }
.map { chapterFile ->
@@ -312,22 +315,21 @@ class LocalSource(
}
}
}
}
.sortedWith { c1, c2 ->
}.sortedWith { c1, c2 ->
val c = c2.chapter_number.compareTo(c1.chapter_number)
if (c == 0) c2.name.compareToCaseInsensitiveNaturalOrder(c1.name) else c
}
.toList()
}
}.toList()
// Filters
override fun getFilterList() = FilterList(OrderBy.Popular())
// TODO Fix Memory Leak
override suspend fun getPageList(chapter: SChapter): List<Page> {
return when (val format = getFormat(chapter)) {
override suspend fun getPageList(chapter: SChapter): List<Page> =
when (val format = getFormat(chapter)) {
is Format.Directory -> {
format.file.listFiles().orEmpty()
format.file
.listFiles()
.orEmpty()
.filter { !it.isDirectory && ImageUtil.isImage(it.name, it::inputStream) }
.sortedWith { f1, f2 -> f1.name.compareToCaseInsensitiveNaturalOrder(f2.name) }
.mapIndexed { index, page ->
@@ -359,11 +361,11 @@ class LocalSource(
pages
}
}
}
fun getFormat(chapter: SChapter): Format {
try {
return fileSystem.getBaseDirectories()
return fileSystem
.getBaseDirectories()
.map { dir -> File(dir, chapter.url) }
.find { it.exists() }
?.let(Format.Companion::valueOf)
@@ -378,21 +380,23 @@ class LocalSource(
private fun updateCover(
chapter: SChapter,
manga: SManga,
): File? {
return try {
): File? =
try {
when (val format = getFormat(chapter)) {
is Format.Directory -> {
val entry =
format.file.listFiles()
format.file
.listFiles()
?.sortedWith { f1, f2 -> f1.name.compareToCaseInsensitiveNaturalOrder(f2.name) }
?.find { !it.isDirectory && ImageUtil.isImage(it.name) { FileInputStream(it) } }
entry?.let { coverManager.update(manga, it.inputStream()) }
}
is Format.Zip -> {
ZipFile(format.file).use { zip ->
ZipFile.builder().setFile(format.file).get().use { zip ->
val entry =
zip.entries.toList()
zip.entries
.toList()
.sortedWith { f1, f2 -> f1.name.compareToCaseInsensitiveNaturalOrder(f2.name) }
.find { !it.isDirectory && ImageUtil.isImage(it.name) { zip.getInputStream(it) } }
@@ -412,7 +416,8 @@ class LocalSource(
is Format.Epub -> {
EpubFile(format.file).use { epub ->
val entry =
epub.getImagesFromPages()
epub
.getImagesFromPages()
.firstOrNull()
?.let { epub.getEntry(it) }
@@ -424,7 +429,6 @@ class LocalSource(
logger.error(e) { "Error updating cover for ${manga.title}" }
null
}
}
companion object {
const val ID = 0L
@@ -2,12 +2,14 @@ package eu.kanade.tachiyomi.source.local.filter
import eu.kanade.tachiyomi.source.model.Filter
sealed class OrderBy(selection: Selection) : Filter.Sort(
"Order by",
arrayOf("Title", "Date"),
selection,
) {
class Popular() : OrderBy(Selection(0, true))
sealed class OrderBy(
selection: Selection,
) : Filter.Sort(
"Order by",
arrayOf("Title", "Date"),
selection,
) {
class Popular : OrderBy(Selection(0, true))
class Latest() : OrderBy(Selection(1, false))
class Latest : OrderBy(Selection(1, false))
}
@@ -11,15 +11,15 @@ private const val DEFAULT_COVER_NAME = "cover.jpg"
class LocalCoverManager(
private val fileSystem: LocalSourceFileSystem,
) {
fun find(mangaUrl: String): File? {
return fileSystem.getFilesInMangaDirectory(mangaUrl)
fun find(mangaUrl: String): File? =
fileSystem
.getFilesInMangaDirectory(mangaUrl)
// Get all file whose names start with 'cover'
.filter { it.isFile && it.nameWithoutExtension.equals("cover", ignoreCase = true) }
// Get the first actual image
.firstOrNull {
ImageUtil.isImage(it.name) { it.inputStream() }
}
}
fun update(
manga: SManga,
@@ -3,13 +3,21 @@ package eu.kanade.tachiyomi.source.local.io
import java.io.File
sealed interface Format {
data class Directory(val file: File) : Format
data class Directory(
val file: File,
) : Format
data class Zip(val file: File) : Format
data class Zip(
val file: File,
) : Format
data class Rar(val file: File) : Format
data class Rar(
val file: File,
) : Format
data class Epub(val file: File) : Format
data class Epub(
val file: File,
) : Format
class UnknownFormatException : Exception()
@@ -6,27 +6,22 @@ import java.io.File
class LocalSourceFileSystem(
private val applicationDirs: ApplicationDirs,
) {
fun getBaseDirectories(): Sequence<File> {
return sequenceOf(File(applicationDirs.localMangaRoot))
}
fun getBaseDirectories(): Sequence<File> = sequenceOf(File(applicationDirs.localMangaRoot))
fun getFilesInBaseDirectories(): Sequence<File> {
return getBaseDirectories()
fun getFilesInBaseDirectories(): Sequence<File> =
getBaseDirectories()
// Get all the files inside all baseDir
.flatMap { it.listFiles().orEmpty().toList() }
}
fun getMangaDirectory(name: String): File? {
return getFilesInBaseDirectories()
fun getMangaDirectory(name: String): File? =
getFilesInBaseDirectories()
// Get the first mangaDir or null
.firstOrNull { it.isDirectory && it.name == name }
}
fun getFilesInMangaDirectory(name: String): Sequence<File> {
return getFilesInBaseDirectories()
fun getFilesInMangaDirectory(name: String): Sequence<File> =
getFilesInBaseDirectories()
// Filter out ones that are not related to the manga and is not a directory
.filter { it.isDirectory && it.name == name }
// Get all the files inside the filtered folders
.flatMap { it.listFiles().orEmpty().toList() }
}
}
@@ -6,18 +6,20 @@ import java.io.File
/**
* Loader used to load a chapter from a .epub file.
*/
class EpubPageLoader(file: File) : PageLoader {
class EpubPageLoader(
file: File,
) : PageLoader {
private val epub = EpubFile(file)
override suspend fun getPages(): List<ReaderPage> {
return epub.getImagesFromPages()
override suspend fun getPages(): List<ReaderPage> =
epub
.getImagesFromPages()
.mapIndexed { i, path ->
val streamFn = { epub.getInputStream(epub.getEntry(path)!!) }
ReaderPage(i).apply {
stream = streamFn
}
}
}
override fun recycle() {
epub.close()
@@ -12,20 +12,21 @@ import java.io.PipedOutputStream
/**
* Loader used to load a chapter from a .rar or .cbr file.
*/
class RarPageLoader(file: File) : PageLoader {
class RarPageLoader(
file: File,
) : PageLoader {
private val rar = Archive(file)
override suspend fun getPages(): List<ReaderPage> {
return rar.fileHeaders.asSequence()
override suspend fun getPages(): List<ReaderPage> =
rar.fileHeaders
.asSequence()
.filter { !it.isDirectory && ImageUtil.isImage(it.fileName) { rar.getInputStream(it) } }
.sortedWith { f1, f2 -> f1.fileName.compareToCaseInsensitiveNaturalOrder(f2.fileName) }
.mapIndexed { i, header ->
ReaderPage(i).apply {
stream = { getStream(rar, header) }
}
}
.toList()
}
}.toList()
override fun recycle() {
rar.close()
@@ -8,20 +8,21 @@ import java.io.File
/**
* Loader used to load a chapter from a .zip or .cbz file.
*/
class ZipPageLoader(file: File) : PageLoader {
private val zip = ZipFile(file)
class ZipPageLoader(
file: File,
) : PageLoader {
private val zip = ZipFile.builder().setFile(file).get()
override suspend fun getPages(): List<ReaderPage> {
return zip.entries.asSequence()
override suspend fun getPages(): List<ReaderPage> =
zip.entries
.asSequence()
.filter { !it.isDirectory && ImageUtil.isImage(it.name) { zip.getInputStream(it) } }
.sortedWith { f1, f2 -> f1.name.compareToCaseInsensitiveNaturalOrder(f2.name) }
.mapIndexed { i, entry ->
ReaderPage(i).apply {
stream = { zip.getInputStream(entry) }
}
}
.toList()
}
}.toList()
override fun recycle() {
zip.close()
@@ -17,8 +17,7 @@ fun SManga.copyFromComicInfo(comicInfo: ComicInfo) {
comicInfo.genre?.value,
comicInfo.tags?.value,
comicInfo.categories?.value,
)
.distinct()
).distinct()
.joinToString(", ") { it.trim() }
.takeIf { it.isNotEmpty() }
?.let { genre = it }
@@ -29,8 +28,7 @@ fun SManga.copyFromComicInfo(comicInfo: ComicInfo) {
comicInfo.colorist?.value,
comicInfo.letterer?.value,
comicInfo.coverArtist?.value,
)
.flatMap { it.split(", ") }
).flatMap { it.split(", ") }
.distinct()
.joinToString(", ") { it.trim() }
.takeIf { it.isNotEmpty() }
@@ -202,14 +200,12 @@ enum class ComicInfoPublishingStatus(
;
companion object {
fun toComicInfoValue(value: Long): String {
return entries.firstOrNull { it.sMangaModelValue == value.toInt() }?.comicInfoValue
fun toComicInfoValue(value: Long): String =
entries.firstOrNull { it.sMangaModelValue == value.toInt() }?.comicInfoValue
?: UNKNOWN.comicInfoValue
}
fun toSMangaValue(value: String?): Int {
return entries.firstOrNull { it.comicInfoValue == value }?.sMangaModelValue
fun toSMangaValue(value: String?): Int =
entries.firstOrNull { it.comicInfoValue == value }?.sMangaModelValue
?: UNKNOWN.sMangaModelValue
}
}
}
@@ -2,20 +2,40 @@ package eu.kanade.tachiyomi.source.model
// The class is originally sealed, Tachidesk adds new subclasses for serialization
// sealed class Filter<T>(val name: String, var state: T) {
open class Filter<T>(val name: String, var state: T) {
open class Header(name: String) : Filter<Any>(name, 0)
open class Filter<T>(
val name: String,
var state: T,
) {
open class Header(
name: String,
) : Filter<Any>(name, 0)
open class Separator(name: String = "") : Filter<Any>(name, 0)
open class Separator(
name: String = "",
) : Filter<Any>(name, 0)
abstract class Select<V>(name: String, val values: Array<V>, state: Int = 0) : Filter<Int>(name, state) {
abstract class Select<V>(
name: String,
val values: Array<V>,
state: Int = 0,
) : Filter<Int>(name, state) {
val displayValues get() = values.map { it.toString() }
}
abstract class Text(name: String, state: String = "") : Filter<String>(name, state)
abstract class Text(
name: String,
state: String = "",
) : Filter<String>(name, state)
abstract class CheckBox(name: String, state: Boolean = false) : Filter<Boolean>(name, state)
abstract class CheckBox(
name: String,
state: Boolean = false,
) : Filter<Boolean>(name, state)
abstract class TriState(name: String, state: Int = STATE_IGNORE) : Filter<Int>(name, state) {
abstract class TriState(
name: String,
state: Int = STATE_IGNORE,
) : Filter<Int>(name, state) {
fun isIgnored() = state == STATE_IGNORE
fun isIncluded() = state == STATE_INCLUDE
@@ -29,11 +49,20 @@ open class Filter<T>(val name: String, var state: T) {
}
}
abstract class Group<V>(name: String, state: List<V>) : Filter<List<V>>(name, state)
abstract class Group<V>(
name: String,
state: List<V>,
) : Filter<List<V>>(name, state)
abstract class Sort(name: String, val values: Array<String>, state: Selection? = null) :
Filter<Sort.Selection?>(name, state) {
data class Selection(val index: Int, val ascending: Boolean)
abstract class Sort(
name: String,
val values: Array<String>,
state: Selection? = null,
) : Filter<Sort.Selection?>(name, state) {
data class Selection(
val index: Int,
val ascending: Boolean,
)
}
override fun equals(other: Any?): Boolean {
@@ -1,5 +1,7 @@
package eu.kanade.tachiyomi.source.model
data class FilterList(val list: List<Filter<*>>) : List<Filter<*>> by list {
data class FilterList(
val list: List<Filter<*>>,
) : List<Filter<*>> by list {
constructor(vararg fs: Filter<*>) : this(if (fs.isNotEmpty()) fs.asList() else emptyList())
}
@@ -1,3 +1,6 @@
package eu.kanade.tachiyomi.source.model
data class MangasPage(val mangas: List<SManga>, val hasNextPage: Boolean)
data class MangasPage(
val mangas: List<SManga>,
val hasNextPage: Boolean,
)
@@ -24,8 +24,6 @@ interface SChapter : Serializable {
}
companion object {
fun create(): SChapter {
return SChapterImpl()
}
fun create(): SChapter = SChapterImpl()
}
}
@@ -62,9 +62,7 @@ interface SManga : Serializable {
const val CANCELLED = 5
const val ON_HIATUS = 6
fun create(): SManga {
return SMangaImpl()
}
fun create(): SManga = SMangaImpl()
}
}
@@ -66,9 +66,7 @@ abstract class HttpSource : CatalogueSource {
open val client: OkHttpClient
get() = network.client
private fun generateId(): Long {
return generateId("${name.lowercase()}/$lang/$versionId")
}
private fun generateId(): Long = generateId("${name.lowercase()}/$lang/$versionId")
/**
* Generates a unique ID for the source based on the provided [name], [lang] and
@@ -121,13 +119,13 @@ abstract class HttpSource : CatalogueSource {
* @param page the page number to retrieve.
*/
@Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getPopularManga"))
override fun fetchPopularManga(page: Int): Observable<MangasPage> {
return client.newCall(popularMangaRequest(page))
override fun fetchPopularManga(page: Int): Observable<MangasPage> =
client
.newCall(popularMangaRequest(page))
.asObservableSuccess()
.map { response ->
popularMangaParse(response)
}
}
/**
* Returns the request for the popular manga given the page.
@@ -156,20 +154,19 @@ abstract class HttpSource : CatalogueSource {
page: Int,
query: String,
filters: FilterList,
): Observable<MangasPage> {
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 ->
): Observable<MangasPage> =
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)
}
}
/**
* Returns the request for the search manga given the page.
@@ -197,13 +194,13 @@ abstract class HttpSource : CatalogueSource {
* @param page the page number to retrieve.
*/
@Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getLatestUpdates"))
override fun fetchLatestUpdates(page: Int): Observable<MangasPage> {
return client.newCall(latestUpdatesRequest(page))
override fun fetchLatestUpdates(page: Int): Observable<MangasPage> =
client
.newCall(latestUpdatesRequest(page))
.asObservableSuccess()
.map { response ->
latestUpdatesParse(response)
}
}
/**
* Returns the request for latest manga given the page.
@@ -227,18 +224,16 @@ abstract class HttpSource : CatalogueSource {
* @return the updated manga.
*/
@Suppress("DEPRECATION")
override suspend fun getMangaDetails(manga: SManga): SManga {
return fetchMangaDetails(manga).awaitSingle()
}
override suspend fun getMangaDetails(manga: SManga): SManga = fetchMangaDetails(manga).awaitSingle()
@Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getMangaDetails"))
override fun fetchMangaDetails(manga: SManga): Observable<SManga> {
return client.newCall(mangaDetailsRequest(manga))
override fun fetchMangaDetails(manga: SManga): Observable<SManga> =
client
.newCall(mangaDetailsRequest(manga))
.asObservableSuccess()
.map { response ->
mangaDetailsParse(response).apply { initialized = true }
}
}
/**
* Returns the request for the details of a manga. Override only if it's needed to change the
@@ -246,9 +241,7 @@ abstract class HttpSource : CatalogueSource {
*
* @param manga the manga to be updated.
*/
open fun mangaDetailsRequest(manga: SManga): Request {
return GET(baseUrl + manga.url, headers)
}
open fun mangaDetailsRequest(manga: SManga): Request = GET(baseUrl + manga.url, headers)
/**
* Parses the response from the site and returns the details of a manga.
@@ -275,9 +268,10 @@ abstract class HttpSource : CatalogueSource {
}
@Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getChapterList"))
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> {
return if (manga.status != SManga.LICENSED) {
client.newCall(chapterListRequest(manga))
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> =
if (manga.status != SManga.LICENSED) {
client
.newCall(chapterListRequest(manga))
.asObservableSuccess()
.map { response ->
chapterListParse(response)
@@ -285,7 +279,6 @@ abstract class HttpSource : CatalogueSource {
} else {
Observable.error(LicensedMangaChaptersException())
}
}
/**
* Returns the request for updating the chapter list. Override only if it's needed to override
@@ -293,9 +286,7 @@ abstract class HttpSource : CatalogueSource {
*
* @param manga the manga to look for chapters.
*/
protected open fun chapterListRequest(manga: SManga): Request {
return GET(baseUrl + manga.url, headers)
}
protected open fun chapterListRequest(manga: SManga): Request = GET(baseUrl + manga.url, headers)
/**
* Parses the response from the site and returns a list of chapters.
@@ -312,18 +303,16 @@ abstract class HttpSource : CatalogueSource {
* @return the pages for the chapter.
*/
@Suppress("DEPRECATION")
override suspend fun getPageList(chapter: SChapter): List<Page> {
return fetchPageList(chapter).awaitSingle()
}
override suspend fun getPageList(chapter: SChapter): List<Page> = fetchPageList(chapter).awaitSingle()
@Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getPageList"))
override fun fetchPageList(chapter: SChapter): Observable<List<Page>> {
return client.newCall(pageListRequest(chapter))
override fun fetchPageList(chapter: SChapter): Observable<List<Page>> =
client
.newCall(pageListRequest(chapter))
.asObservableSuccess()
.map { response ->
pageListParse(response)
}
}
/**
* Returns the request for getting the page list. Override only if it's needed to override the
@@ -331,9 +320,7 @@ abstract class HttpSource : CatalogueSource {
*
* @param chapter the chapter whose page list has to be fetched.
*/
protected open fun pageListRequest(chapter: SChapter): Request {
return GET(baseUrl + chapter.url, headers)
}
protected open fun pageListRequest(chapter: SChapter): Request = GET(baseUrl + chapter.url, headers)
/**
* Parses the response from the site and returns a list of pages.
@@ -350,16 +337,14 @@ abstract class HttpSource : CatalogueSource {
* @param page the page whose source image has to be fetched.
*/
@Suppress("DEPRECATION")
open suspend fun getImageUrl(page: Page): String {
return fetchImageUrl(page).awaitSingle()
}
open suspend fun getImageUrl(page: Page): String = fetchImageUrl(page).awaitSingle()
@Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getImageUrl"))
open fun fetchImageUrl(page: Page): Observable<String> {
return client.newCall(imageUrlRequest(page))
open fun fetchImageUrl(page: Page): Observable<String> =
client
.newCall(imageUrlRequest(page))
.asObservableSuccess()
.map { imageUrlParse(it) }
}
/**
* Returns the request for getting the url to the source image. Override only if it's needed to
@@ -367,9 +352,7 @@ abstract class HttpSource : CatalogueSource {
*
* @param page the chapter whose page list has to be fetched
*/
protected open fun imageUrlRequest(page: Page): Request {
return GET(page.url, headers)
}
protected open fun imageUrlRequest(page: Page): Request = GET(page.url, headers)
/**
* Parses the response from the site and returns the absolute url to the source image.
@@ -385,10 +368,10 @@ abstract class HttpSource : CatalogueSource {
* @since extensions-lib 1.5
* @param page the page whose source image has to be downloaded.
*/
open suspend fun getImage(page: Page): Response {
return client.newCachelessCallWithProgress(imageRequest(page), page)
open suspend fun getImage(page: Page): Response =
client
.newCachelessCallWithProgress(imageRequest(page), page)
.awaitSuccess()
}
/**
* Returns the request for getting the source image. Override only if it's needed to override
@@ -396,9 +379,7 @@ abstract class HttpSource : CatalogueSource {
*
* @param page the chapter whose page list has to be fetched
*/
protected open fun imageRequest(page: Page): Request {
return GET(page.imageUrl!!, headers)
}
protected open fun imageRequest(page: Page): Request = GET(page.imageUrl!!, headers)
/**
* Assigns the url of the chapter without the scheme and domain. It saves some redundancy from
@@ -425,8 +406,8 @@ abstract class HttpSource : CatalogueSource {
*
* @param orig the full url.
*/
private fun getUrlWithoutDomain(orig: String): String {
return try {
private fun getUrlWithoutDomain(orig: String): String =
try {
val uri = URI(orig.replace(" ", "%20"))
var out = uri.path
if (uri.query != null) {
@@ -439,7 +420,6 @@ abstract class HttpSource : CatalogueSource {
} catch (e: URISyntaxException) {
orig
}
}
/**
* Returns the url of the provided manga
@@ -448,9 +428,7 @@ abstract class HttpSource : CatalogueSource {
* @param manga the manga
* @return url of the manga
*/
open fun getMangaUrl(manga: SManga): String {
return mangaDetailsRequest(manga).url.toString()
}
open fun getMangaUrl(manga: SManga): String = mangaDetailsRequest(manga).url.toString()
/**
* Returns the url of the provided chapter
@@ -459,9 +437,7 @@ abstract class HttpSource : CatalogueSource {
* @param chapter the chapter
* @return url of the chapter
*/
open fun getChapterUrl(chapter: SChapter): String {
return pageListRequest(chapter).url.toString()
}
open fun getChapterUrl(chapter: SChapter): String = pageListRequest(chapter).url.toString()
/**
* Called before inserting a new chapter into database. Use it if you need to override chapter
@@ -138,9 +138,7 @@ abstract class ParsedHttpSource : HttpSource() {
*
* @param response the response from the site.
*/
override fun mangaDetailsParse(response: Response): SManga {
return mangaDetailsParse(response.asJsoup())
}
override fun mangaDetailsParse(response: Response): SManga = mangaDetailsParse(response.asJsoup())
/**
* Returns the details of the manga from the given [document].
@@ -176,9 +174,7 @@ abstract class ParsedHttpSource : HttpSource() {
*
* @param response the response from the site.
*/
override fun pageListParse(response: Response): List<Page> {
return pageListParse(response.asJsoup())
}
override fun pageListParse(response: Response): List<Page> = pageListParse(response.asJsoup())
/**
* Returns a page list from the given document.
@@ -192,9 +188,7 @@ abstract class ParsedHttpSource : HttpSource() {
*
* @param response the response from the site.
*/
override fun imageUrlParse(response: Response): String {
return imageUrlParse(response.asJsoup())
}
override fun imageUrlParse(response: Response): String = imageUrlParse(response.asJsoup())
/**
* Returns the absolute url to the source image from the document.
@@ -8,25 +8,17 @@ import org.jsoup.nodes.Element
fun Element.selectText(
css: String,
defaultValue: String? = null,
): String? {
return select(css).first()?.text() ?: defaultValue
}
): String? = select(css).first()?.text() ?: defaultValue
fun Element.selectInt(
css: String,
defaultValue: Int = 0,
): Int {
return select(css).first()?.text()?.toInt() ?: defaultValue
}
): Int = select(css).first()?.text()?.toInt() ?: defaultValue
fun Element.attrOrText(css: String): String {
return if (css != "text") attr(css) else text()
}
fun Element.attrOrText(css: String): String = if (css != "text") attr(css) else text()
/**
* Returns a Jsoup document for this response.
* @param html the body of the response. Use only if the body was read before calling this method.
*/
fun Response.asJsoup(html: String? = null): Document {
return Jsoup.parse(html ?: body.string(), request.url.toString())
}
fun Response.asJsoup(html: String? = null): Document = Jsoup.parse(html ?: body.string(), request.url.toString())
@@ -68,15 +68,14 @@ object ChapterRecognition {
* @param match result of regex
* @return chapter number if found else null
*/
private fun getChapterNumberFromMatch(match: MatchResult): Double {
return match.let {
private fun getChapterNumberFromMatch(match: MatchResult): Double =
match.let {
val initial = it.groups[1]?.value?.toDouble()!!
val subChapterDecimal = it.groups[2]?.value
val subChapterAlpha = it.groups[3]?.value
val addition = checkForDecimal(subChapterDecimal, subChapterAlpha)
initial.plus(addition)
}
}
/**
* Check for decimal in received strings
@@ -1,11 +1,10 @@
package eu.kanade.tachiyomi.util.chapter
object ChapterSanitizer {
fun String.sanitize(title: String): String {
return trim()
fun String.sanitize(title: String): String =
trim()
.removePrefix(title)
.trim(*CHAPTER_TRIM_CHARS)
}
private val CHAPTER_TRIM_CHARS =
arrayOf(
@@ -5,29 +5,35 @@ import java.security.MessageDigest
object Hash {
private val chars =
charArrayOf(
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'a', 'b', 'c', 'd', 'e', 'f',
'0',
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
'a',
'b',
'c',
'd',
'e',
'f',
)
private val MD5 get() = MessageDigest.getInstance("MD5")
private val SHA256 get() = MessageDigest.getInstance("SHA-256")
fun sha256(bytes: ByteArray): String {
return encodeHex(SHA256.digest(bytes))
}
fun sha256(bytes: ByteArray): String = encodeHex(SHA256.digest(bytes))
fun sha256(string: String): String {
return sha256(string.toByteArray())
}
fun sha256(string: String): String = sha256(string.toByteArray())
fun md5(bytes: ByteArray): String {
return encodeHex(MD5.digest(bytes))
}
fun md5(bytes: ByteArray): String = encodeHex(MD5.digest(bytes))
fun md5(string: String): String {
return md5(string.toByteArray())
}
fun md5(string: String): String = md5(string.toByteArray())
private fun encodeHex(data: ByteArray): String {
val l = data.size
@@ -10,13 +10,12 @@ import kotlin.math.floor
fun String.chop(
count: Int,
replacement: String = "",
): String {
return if (length > count) {
): String =
if (length > count) {
take(count - replacement.length) + replacement
} else {
this
}
}
/**
* Replaces the given string to have at most [count] characters using [replacement] near the center.
@@ -46,9 +45,7 @@ fun String.compareToCaseInsensitiveNaturalOrder(other: String): Int {
/**
* Returns the size of the string as the number of bytes.
*/
fun String.byteSize(): Int {
return toByteArray(Charsets.UTF_8).size
}
fun String.byteSize(): Int = toByteArray(Charsets.UTF_8).size
/**
* Returns a string containing the first [n] bytes from this string, or the entire string if this
@@ -11,11 +11,13 @@ import java.io.InputStream
/**
* Wrapper over ZipFile to load files in epub format.
*/
class EpubFile(file: File) : Closeable {
class EpubFile(
file: File,
) : Closeable {
/**
* Zip file of this epub.
*/
private val zip = ZipFile(file)
private val zip = ZipFile.builder().setFile(file).get()
/**
* Path separator used by this epub.
@@ -32,16 +34,12 @@ class EpubFile(file: File) : Closeable {
/**
* Returns an input stream for reading the contents of the specified zip file entry.
*/
fun getInputStream(entry: ZipArchiveEntry): InputStream {
return zip.getInputStream(entry)
}
fun getInputStream(entry: ZipArchiveEntry): InputStream = zip.getInputStream(entry)
/**
* Returns the zip file entry for the specified name, or null if not found.
*/
fun getEntry(name: String): ZipArchiveEntry? {
return zip.getEntry(name)
}
fun getEntry(name: String): ZipArchiveEntry? = zip.getEntry(name)
/**
* Returns the path of all the images found in the epub file.
@@ -81,7 +79,8 @@ class EpubFile(file: File) : Closeable {
*/
fun getPagesFromDocument(document: Document): List<String> {
val pages =
document.select("manifest > item")
document
.select("manifest > item")
.filter { node -> "application/xhtml+xml" == node.attr("media-type") }
.associateBy { it.attr("id") }
@@ -20,8 +20,8 @@ data class AboutDataClass(
)
object About {
fun getAbout(): AboutDataClass {
return AboutDataClass(
fun getAbout(): AboutDataClass =
AboutDataClass(
BuildConfig.NAME,
BuildConfig.VERSION,
BuildConfig.REVISION,
@@ -30,5 +30,4 @@ object About {
BuildConfig.GITHUB,
BuildConfig.DISCORD,
)
}
}
@@ -31,18 +31,26 @@ object AppUpdate {
suspend fun checkUpdate(): List<UpdateDataClass> {
val stableJson =
json.parseToJsonElement(
network.client.newCall(
GET(LATEST_STABLE_CHANNEL_URL),
).await().body.string(),
).jsonObject
json
.parseToJsonElement(
network.client
.newCall(
GET(LATEST_STABLE_CHANNEL_URL),
).await()
.body
.string(),
).jsonObject
val previewJson =
json.parseToJsonElement(
network.client.newCall(
GET(LATEST_PREVIEW_CHANNEL_URL),
).await().body.string(),
).jsonObject
json
.parseToJsonElement(
network.client
.newCall(
GET(LATEST_PREVIEW_CHANNEL_URL),
).await()
.body
.string(),
).jsonObject
return listOf(
UpdateDataClass(
@@ -38,10 +38,10 @@ object GlobalMeta {
}
}
fun getMetaMap(): Map<String, String> {
return transaction {
GlobalMetaTable.selectAll()
fun getMetaMap(): Map<String, String> =
transaction {
GlobalMetaTable
.selectAll()
.associate { it[GlobalMetaTable.key] to it[GlobalMetaTable.value] }
}
}
}
@@ -14,12 +14,14 @@ inline fun <T> asDataFetcherResult(block: () -> T): DataFetcherResult<T?> {
if (result.isFailure) {
logger.error(result.exceptionOrNull()) { "asDataFetcherResult: failed due to" }
return DataFetcherResult.newResult<T?>()
return DataFetcherResult
.newResult<T?>()
.error(result.exceptionOrNull()?.toGraphQLError())
.build()
}
return DataFetcherResult.newResult<T?>()
return DataFetcherResult
.newResult<T?>()
.data(result.getOrNull())
.build()
}
@@ -10,21 +10,13 @@ class CustomCacheMap<K, V> : CacheMap<K, V> {
cache = HashMap()
}
override fun containsKey(key: K): Boolean {
return cache.containsKey(key)
}
override fun containsKey(key: K): Boolean = cache.containsKey(key)
override fun get(key: K): CompletableFuture<V> {
return cache[key]!!
}
override fun get(key: K): CompletableFuture<V> = cache[key]!!
fun getKeys(): Collection<K> {
return cache.keys.toSet()
}
fun getKeys(): Collection<K> = cache.keys.toSet()
override fun getAll(): Collection<CompletableFuture<V>> {
return cache.values
}
override fun getAll(): Collection<CompletableFuture<V>> = cache.values
override fun set(
key: K,
@@ -30,7 +30,8 @@ class CategoryDataLoader : KotlinDataLoader<Int, CategoryType> {
transaction {
addLogger(Slf4jSqlDebugLogger)
val categories =
CategoryTable.select { CategoryTable.id inList ids }
CategoryTable
.select { CategoryTable.id inList ids }
.map { CategoryType(it) }
.associateBy { it.id }
ids.map { categories[it] }
@@ -66,7 +67,8 @@ class CategoriesForMangaDataLoader : KotlinDataLoader<Int, CategoryNodeList> {
transaction {
addLogger(Slf4jSqlDebugLogger)
val itemsByRef =
CategoryMangaTable.innerJoin(CategoryTable)
CategoryMangaTable
.innerJoin(CategoryTable)
.select { CategoryMangaTable.manga inList ids }
.map { Pair(it[CategoryMangaTable.manga].value, CategoryType(it)) }
.groupBy { it.first }
@@ -32,7 +32,8 @@ class ChapterDataLoader : KotlinDataLoader<Int, ChapterType?> {
transaction {
addLogger(Slf4jSqlDebugLogger)
val chapters =
ChapterTable.select { ChapterTable.id inList ids }
ChapterTable
.select { ChapterTable.id inList ids }
.map { ChapterType(it) }
.associateBy { it.id }
ids.map { chapters[it] }
@@ -50,7 +51,8 @@ class ChaptersForMangaDataLoader : KotlinDataLoader<Int, ChapterNodeList> {
transaction {
addLogger(Slf4jSqlDebugLogger)
val chaptersByMangaId =
ChapterTable.select { ChapterTable.manga inList ids }
ChapterTable
.select { ChapterTable.manga inList ids }
.map { ChapterType(it) }
.groupBy { it.mangaId }
ids.map { (chaptersByMangaId[it] ?: emptyList()).toNodeList() }
@@ -128,7 +130,8 @@ class HasDuplicateChaptersForMangaDataLoader : KotlinDataLoader<Int, Boolean> {
transaction {
addLogger(Slf4jSqlDebugLogger)
val duplicatedChapterCountByMangaId =
ChapterTable.slice(ChapterTable.manga, ChapterTable.chapter_number, ChapterTable.chapter_number.count())
ChapterTable
.slice(ChapterTable.manga, ChapterTable.chapter_number, ChapterTable.chapter_number.count())
.select { (ChapterTable.manga inList ids) and (ChapterTable.chapter_number greaterEq 0f) }
.groupBy(ChapterTable.manga, ChapterTable.chapter_number)
.having { ChapterTable.chapter_number.count() greater 1 }
@@ -28,7 +28,8 @@ class ExtensionDataLoader : KotlinDataLoader<String, ExtensionType?> {
transaction {
addLogger(Slf4jSqlDebugLogger)
val extensions =
ExtensionTable.select { ExtensionTable.pkgName inList ids }
ExtensionTable
.select { ExtensionTable.pkgName inList ids }
.map { ExtensionType(it) }
.associateBy { it.pkgName }
ids.map { extensions[it] }
@@ -46,7 +47,8 @@ class ExtensionForSourceDataLoader : KotlinDataLoader<Long, ExtensionType?> {
transaction {
addLogger(Slf4jSqlDebugLogger)
val extensions =
ExtensionTable.innerJoin(SourceTable)
ExtensionTable
.innerJoin(SourceTable)
.select { SourceTable.id inList ids }
.toList()
.map { Triple(it[SourceTable.id].value, it[ExtensionTable.pkgName], it) }
@@ -33,7 +33,8 @@ class MangaDataLoader : KotlinDataLoader<Int, MangaType?> {
transaction {
addLogger(Slf4jSqlDebugLogger)
val manga =
MangaTable.select { MangaTable.id inList ids }
MangaTable
.select { MangaTable.id inList ids }
.map { MangaType(it) }
.associateBy { it.id }
ids.map { manga[it] }
@@ -63,7 +64,8 @@ class MangaForCategoryDataLoader : KotlinDataLoader<Int, MangaNodeList> {
} else {
emptyMap()
} +
CategoryMangaTable.innerJoin(MangaTable)
CategoryMangaTable
.innerJoin(MangaTable)
.select { CategoryMangaTable.category inList ids }
.map { Pair(it[CategoryMangaTable.category].value, MangaType(it)) }
.groupBy { it.first }
@@ -84,7 +86,8 @@ class MangaForSourceDataLoader : KotlinDataLoader<Long, MangaNodeList> {
transaction {
addLogger(Slf4jSqlDebugLogger)
val mangaBySourceId =
MangaTable.select { MangaTable.sourceReference inList ids }
MangaTable
.select { MangaTable.sourceReference inList ids }
.map { MangaType(it) }
.groupBy { it.sourceId }
ids.map { (mangaBySourceId[it] ?: emptyList()).toNodeList() }
@@ -104,7 +107,8 @@ class MangaForIdsDataLoader : KotlinDataLoader<List<Int>, MangaNodeList> {
addLogger(Slf4jSqlDebugLogger)
val ids = mangaIds.flatten().distinct()
val manga =
MangaTable.select { MangaTable.id inList ids }
MangaTable
.select { MangaTable.id inList ids }
.map { MangaType(it) }
mangaIds.map { mangaIds ->
manga.filter { it.id in mangaIds }.toNodeList()
@@ -28,7 +28,8 @@ class GlobalMetaDataLoader : KotlinDataLoader<String, GlobalMetaType?> {
transaction {
addLogger(Slf4jSqlDebugLogger)
val metasByRefId =
GlobalMetaTable.select { GlobalMetaTable.key inList ids }
GlobalMetaTable
.select { GlobalMetaTable.key inList ids }
.map { GlobalMetaType(it) }
.associateBy { it.key }
ids.map { metasByRefId[it] }
@@ -46,7 +47,8 @@ class ChapterMetaDataLoader : KotlinDataLoader<Int, List<ChapterMetaType>> {
transaction {
addLogger(Slf4jSqlDebugLogger)
val metasByRefId =
ChapterMetaTable.select { ChapterMetaTable.ref inList ids }
ChapterMetaTable
.select { ChapterMetaTable.ref inList ids }
.map { ChapterMetaType(it) }
.groupBy { it.chapterId }
ids.map { metasByRefId[it].orEmpty() }
@@ -64,7 +66,8 @@ class MangaMetaDataLoader : KotlinDataLoader<Int, List<MangaMetaType>> {
transaction {
addLogger(Slf4jSqlDebugLogger)
val metasByRefId =
MangaMetaTable.select { MangaMetaTable.ref inList ids }
MangaMetaTable
.select { MangaMetaTable.ref inList ids }
.map { MangaMetaType(it) }
.groupBy { it.mangaId }
ids.map { metasByRefId[it].orEmpty() }
@@ -82,7 +85,8 @@ class CategoryMetaDataLoader : KotlinDataLoader<Int, List<CategoryMetaType>> {
transaction {
addLogger(Slf4jSqlDebugLogger)
val metasByRefId =
CategoryMetaTable.select { CategoryMetaTable.ref inList ids }
CategoryMetaTable
.select { CategoryMetaTable.ref inList ids }
.map { CategoryMetaType(it) }
.groupBy { it.categoryId }
ids.map { metasByRefId[it].orEmpty() }
@@ -100,7 +104,8 @@ class SourceMetaDataLoader : KotlinDataLoader<Long, List<SourceMetaType>> {
transaction {
addLogger(Slf4jSqlDebugLogger)
val metasByRefId =
SourceMetaTable.select { SourceMetaTable.ref inList ids }
SourceMetaTable
.select { SourceMetaTable.ref inList ids }
.map { SourceMetaType(it) }
.groupBy { it.sourceId }
ids.map { metasByRefId[it].orEmpty() }
@@ -30,7 +30,8 @@ class SourceDataLoader : KotlinDataLoader<Long, SourceType?> {
transaction {
addLogger(Slf4jSqlDebugLogger)
val source =
SourceTable.select { SourceTable.id inList ids }
SourceTable
.select { SourceTable.id inList ids }
.mapNotNull { SourceType(it) }
.associateBy { it.id }
ids.map { source[it] }
@@ -49,7 +50,8 @@ class SourcesForExtensionDataLoader : KotlinDataLoader<String, SourceNodeList> {
addLogger(Slf4jSqlDebugLogger)
val sourcesByExtensionPkg =
SourceTable.innerJoin(ExtensionTable)
SourceTable
.innerJoin(ExtensionTable)
.select { ExtensionTable.pkgName inList ids }
.map { Pair(it[ExtensionTable.pkgName], SourceType(it)) }
.groupBy { it.first }
@@ -89,7 +89,8 @@ class TrackRecordsForMangaIdDataLoader : KotlinDataLoader<Int, TrackRecordNodeLi
transaction {
addLogger(Slf4jSqlDebugLogger)
val trackRecordsByMangaId =
TrackRecordTable.select { TrackRecordTable.mangaId inList ids }
TrackRecordTable
.select { TrackRecordTable.mangaId inList ids }
.map { TrackRecordType(it) }
.groupBy { it.mangaId }
ids.map { (trackRecordsByMangaId[it] ?: emptyList()).toNodeList() }
@@ -107,7 +108,8 @@ class DisplayScoreForTrackRecordDataLoader : KotlinDataLoader<Int, String> {
transaction {
addLogger(Slf4jSqlDebugLogger)
val trackRecords =
TrackRecordTable.select { TrackRecordTable.id inList ids }
TrackRecordTable
.select { TrackRecordTable.id inList ids }
.toList()
.map { it.toTrack() }
.associateBy { it.id!! }
@@ -128,7 +130,8 @@ class TrackRecordsForTrackerIdDataLoader : KotlinDataLoader<Int, TrackRecordNode
transaction {
addLogger(Slf4jSqlDebugLogger)
val trackRecordsBySyncId =
TrackRecordTable.select { TrackRecordTable.trackerId inList ids }
TrackRecordTable
.select { TrackRecordTable.trackerId inList ids }
.map { TrackRecordType(it) }
.groupBy { it.mangaId }
ids.map { (trackRecordsBySyncId[it] ?: emptyList()).toNodeList() }
@@ -146,7 +149,8 @@ class TrackRecordDataLoader : KotlinDataLoader<Int, TrackRecordType> {
transaction {
addLogger(Slf4jSqlDebugLogger)
val trackRecordsId =
TrackRecordTable.select { TrackRecordTable.id inList ids }
TrackRecordTable
.select { TrackRecordTable.id inList ids }
.map { TrackRecordType(it) }
.associateBy { it.id }
ids.map { trackRecordsId[it] }
@@ -38,15 +38,14 @@ class CategoryMutation {
val meta: CategoryMetaType,
)
fun setCategoryMeta(input: SetCategoryMetaInput): DataFetcherResult<SetCategoryMetaPayload?> {
return asDataFetcherResult {
fun setCategoryMeta(input: SetCategoryMetaInput): DataFetcherResult<SetCategoryMetaPayload?> =
asDataFetcherResult {
val (clientMutationId, meta) = input
Category.modifyMeta(meta.categoryId, meta.key, meta.value)
SetCategoryMetaPayload(clientMutationId, meta)
}
}
data class DeleteCategoryMetaInput(
val clientMutationId: String? = null,
@@ -60,14 +59,15 @@ class CategoryMutation {
val category: CategoryType,
)
fun deleteCategoryMeta(input: DeleteCategoryMetaInput): DataFetcherResult<DeleteCategoryMetaPayload?> {
return asDataFetcherResult {
fun deleteCategoryMeta(input: DeleteCategoryMetaInput): DataFetcherResult<DeleteCategoryMetaPayload?> =
asDataFetcherResult {
val (clientMutationId, categoryId, key) = input
val (meta, category) =
transaction {
val meta =
CategoryMetaTable.select { (CategoryMetaTable.ref eq categoryId) and (CategoryMetaTable.key eq key) }
CategoryMetaTable
.select { (CategoryMetaTable.ref eq categoryId) and (CategoryMetaTable.key eq key) }
.firstOrNull()
CategoryMetaTable.deleteWhere { (CategoryMetaTable.ref eq categoryId) and (CategoryMetaTable.key eq key) }
@@ -86,7 +86,6 @@ class CategoryMutation {
DeleteCategoryMetaPayload(clientMutationId, meta, category)
}
}
data class UpdateCategoryPatch(
val name: String? = null,
@@ -153,8 +152,8 @@ class CategoryMutation {
}
}
fun updateCategory(input: UpdateCategoryInput): DataFetcherResult<UpdateCategoryPayload?> {
return asDataFetcherResult {
fun updateCategory(input: UpdateCategoryInput): DataFetcherResult<UpdateCategoryPayload?> =
asDataFetcherResult {
val (clientMutationId, id, patch) = input
updateCategories(listOf(id), patch)
@@ -169,10 +168,9 @@ class CategoryMutation {
category = category,
)
}
}
fun updateCategories(input: UpdateCategoriesInput): DataFetcherResult<UpdateCategoriesPayload?> {
return asDataFetcherResult {
fun updateCategories(input: UpdateCategoriesInput): DataFetcherResult<UpdateCategoriesPayload?> =
asDataFetcherResult {
val (clientMutationId, ids, patch) = input
updateCategories(ids, patch)
@@ -187,7 +185,6 @@ class CategoryMutation {
categories = categories,
)
}
}
data class UpdateCategoryOrderPayload(
val clientMutationId: String?,
@@ -200,8 +197,8 @@ class CategoryMutation {
val position: Int,
)
fun updateCategoryOrder(input: UpdateCategoryOrderInput): DataFetcherResult<UpdateCategoryOrderPayload?> {
return asDataFetcherResult {
fun updateCategoryOrder(input: UpdateCategoryOrderInput): DataFetcherResult<UpdateCategoryOrderPayload?> =
asDataFetcherResult {
val (clientMutationId, categoryId, position) = input
require(position > 0) {
"'order' must not be <= 0"
@@ -242,7 +239,6 @@ class CategoryMutation {
categories = categories,
)
}
}
data class CreateCategoryInput(
val clientMutationId: String? = null,
@@ -258,8 +254,8 @@ class CategoryMutation {
val category: CategoryType,
)
fun createCategory(input: CreateCategoryInput): DataFetcherResult<CreateCategoryPayload?> {
return asDataFetcherResult {
fun createCategory(input: CreateCategoryInput): DataFetcherResult<CreateCategoryPayload?> =
asDataFetcherResult {
val (clientMutationId, name, order, default, includeInUpdate, includeInDownload) = input
transaction {
require(CategoryTable.select { CategoryTable.name eq input.name }.isEmpty()) {
@@ -305,7 +301,6 @@ class CategoryMutation {
CreateCategoryPayload(clientMutationId, category)
}
}
data class DeleteCategoryInput(
val clientMutationId: String? = null,
@@ -332,12 +327,14 @@ class CategoryMutation {
val (category, mangas) =
transaction {
val category =
CategoryTable.select { CategoryTable.id eq categoryId }
CategoryTable
.select { CategoryTable.id eq categoryId }
.firstOrNull()
val mangas =
transaction {
MangaTable.innerJoin(CategoryMangaTable)
MangaTable
.innerJoin(CategoryMangaTable)
.select { CategoryMangaTable.category eq categoryId }
.map { MangaType(it) }
}
@@ -403,9 +400,10 @@ class CategoryMutation {
ids.filter { it != DEFAULT_CATEGORY_ID }.forEach { mangaId ->
patch.addToCategories.forEach { categoryId ->
val existingMapping =
CategoryMangaTable.select {
(CategoryMangaTable.manga eq mangaId) and (CategoryMangaTable.category eq categoryId)
}.isNotEmpty()
CategoryMangaTable
.select {
(CategoryMangaTable.manga eq mangaId) and (CategoryMangaTable.category eq categoryId)
}.isNotEmpty()
if (!existingMapping) {
add(mangaId to categoryId)
@@ -422,8 +420,8 @@ class CategoryMutation {
}
}
fun updateMangaCategories(input: UpdateMangaCategoriesInput): DataFetcherResult<UpdateMangaCategoriesPayload?> {
return asDataFetcherResult {
fun updateMangaCategories(input: UpdateMangaCategoriesInput): DataFetcherResult<UpdateMangaCategoriesPayload?> =
asDataFetcherResult {
val (clientMutationId, id, patch) = input
updateMangas(listOf(id), patch)
@@ -438,10 +436,9 @@ class CategoryMutation {
manga = manga,
)
}
}
fun updateMangasCategories(input: UpdateMangasCategoriesInput): DataFetcherResult<UpdateMangasCategoriesPayload?> {
return asDataFetcherResult {
fun updateMangasCategories(input: UpdateMangasCategoriesInput): DataFetcherResult<UpdateMangasCategoriesPayload?> =
asDataFetcherResult {
val (clientMutationId, ids, patch) = input
updateMangas(ids, patch)
@@ -456,5 +453,4 @@ class CategoryMutation {
mangas = mangas,
)
}
}
}
@@ -95,8 +95,8 @@ class ChapterMutation {
}
}
fun updateChapter(input: UpdateChapterInput): DataFetcherResult<UpdateChapterPayload?> {
return asDataFetcherResult {
fun updateChapter(input: UpdateChapterInput): DataFetcherResult<UpdateChapterPayload?> =
asDataFetcherResult {
val (clientMutationId, id, patch) = input
updateChapters(listOf(id), patch)
@@ -111,10 +111,9 @@ class ChapterMutation {
chapter = chapter,
)
}
}
fun updateChapters(input: UpdateChaptersInput): DataFetcherResult<UpdateChaptersPayload?> {
return asDataFetcherResult {
fun updateChapters(input: UpdateChaptersInput): DataFetcherResult<UpdateChaptersPayload?> =
asDataFetcherResult {
val (clientMutationId, ids, patch) = input
updateChapters(ids, patch)
@@ -129,7 +128,6 @@ class ChapterMutation {
chapters = chapters,
)
}
}
data class FetchChaptersInput(
val clientMutationId: String? = null,
@@ -150,7 +148,8 @@ class ChapterMutation {
val chapters =
transaction {
ChapterTable.select { ChapterTable.manga eq mangaId }
ChapterTable
.select { ChapterTable.manga eq mangaId }
.orderBy(ChapterTable.sourceOrder)
.map { ChapterType(it) }
}
@@ -173,15 +172,14 @@ class ChapterMutation {
val meta: ChapterMetaType,
)
fun setChapterMeta(input: SetChapterMetaInput): DataFetcherResult<SetChapterMetaPayload?> {
return asDataFetcherResult {
fun setChapterMeta(input: SetChapterMetaInput): DataFetcherResult<SetChapterMetaPayload?> =
asDataFetcherResult {
val (clientMutationId, meta) = input
Chapter.modifyChapterMeta(meta.chapterId, meta.key, meta.value)
SetChapterMetaPayload(clientMutationId, meta)
}
}
data class DeleteChapterMetaInput(
val clientMutationId: String? = null,
@@ -195,14 +193,15 @@ class ChapterMutation {
val chapter: ChapterType,
)
fun deleteChapterMeta(input: DeleteChapterMetaInput): DataFetcherResult<DeleteChapterMetaPayload?> {
return asDataFetcherResult {
fun deleteChapterMeta(input: DeleteChapterMetaInput): DataFetcherResult<DeleteChapterMetaPayload?> =
asDataFetcherResult {
val (clientMutationId, chapterId, key) = input
val (meta, chapter) =
transaction {
val meta =
ChapterMetaTable.select { (ChapterMetaTable.ref eq chapterId) and (ChapterMetaTable.key eq key) }
ChapterMetaTable
.select { (ChapterMetaTable.ref eq chapterId) and (ChapterMetaTable.key eq key) }
.firstOrNull()
ChapterMetaTable.deleteWhere { (ChapterMetaTable.ref eq chapterId) and (ChapterMetaTable.key eq key) }
@@ -221,7 +220,6 @@ class ChapterMutation {
DeleteChapterMetaPayload(clientMutationId, meta, chapter)
}
}
data class FetchChapterPagesInput(
val clientMutationId: String? = null,
@@ -37,7 +37,8 @@ class DownloadMutation {
clientMutationId = clientMutationId,
chapters =
transaction {
ChapterTable.select { ChapterTable.id inList chapters }
ChapterTable
.select { ChapterTable.id inList chapters }
.map { ChapterType(it) }
},
)
@@ -195,8 +196,8 @@ class DownloadMutation {
val downloadStatus: DownloadStatus,
)
fun startDownloader(input: StartDownloaderInput): CompletableFuture<DataFetcherResult<StartDownloaderPayload?>> {
return future {
fun startDownloader(input: StartDownloaderInput): CompletableFuture<DataFetcherResult<StartDownloaderPayload?>> =
future {
asDataFetcherResult {
DownloadManager.start()
@@ -211,7 +212,6 @@ class DownloadMutation {
)
}
}
}
data class StopDownloaderInput(
val clientMutationId: String? = null,
@@ -222,8 +222,8 @@ class DownloadMutation {
val downloadStatus: DownloadStatus,
)
fun stopDownloader(input: StopDownloaderInput): CompletableFuture<DataFetcherResult<StopDownloaderPayload?>> {
return future {
fun stopDownloader(input: StopDownloaderInput): CompletableFuture<DataFetcherResult<StopDownloaderPayload?>> =
future {
asDataFetcherResult {
DownloadManager.stop()
@@ -238,7 +238,6 @@ class DownloadMutation {
)
}
}
}
data class ClearDownloaderInput(
val clientMutationId: String? = null,
@@ -249,8 +248,8 @@ class DownloadMutation {
val downloadStatus: DownloadStatus,
)
fun clearDownloader(input: ClearDownloaderInput): CompletableFuture<DataFetcherResult<ClearDownloaderPayload?>> {
return future {
fun clearDownloader(input: ClearDownloaderInput): CompletableFuture<DataFetcherResult<ClearDownloaderPayload?>> =
future {
asDataFetcherResult {
DownloadManager.clear()
@@ -265,7 +264,6 @@ class DownloadMutation {
)
}
}
}
data class ReorderChapterDownloadInput(
val clientMutationId: String? = null,
@@ -48,7 +48,8 @@ class ExtensionMutation {
) {
val extensions =
transaction {
ExtensionTable.select { ExtensionTable.pkgName inList ids }
ExtensionTable
.select { ExtensionTable.pkgName inList ids }
.map { ExtensionType(it) }
}
@@ -80,7 +81,9 @@ class ExtensionMutation {
val extension =
transaction {
ExtensionTable.select { ExtensionTable.pkgName eq id }.firstOrNull()
ExtensionTable
.select { ExtensionTable.pkgName eq id }
.firstOrNull()
?.let { ExtensionType(it) }
}
@@ -101,7 +104,8 @@ class ExtensionMutation {
val extensions =
transaction {
ExtensionTable.select { ExtensionTable.pkgName inList ids }
ExtensionTable
.select { ExtensionTable.pkgName inList ids }
.map { ExtensionType(it) }
}
@@ -131,7 +135,8 @@ class ExtensionMutation {
val extensions =
transaction {
ExtensionTable.select { ExtensionTable.name neq LocalSource.EXTENSION_NAME }
ExtensionTable
.select { ExtensionTable.name neq LocalSource.EXTENSION_NAME }
.map { ExtensionType(it) }
}
@@ -59,8 +59,8 @@ class InfoMutation {
}
}
fun resetWebUIUpdateStatus(): CompletableFuture<DataFetcherResult<WebUIUpdateStatus?>> {
return future {
fun resetWebUIUpdateStatus(): CompletableFuture<DataFetcherResult<WebUIUpdateStatus?>> =
future {
asDataFetcherResult {
withTimeout(30.seconds) {
val isUpdateFinished = WebInterfaceManager.status.value.state != DOWNLOADING
@@ -74,5 +74,4 @@ class InfoMutation {
}
}
}
}
}
@@ -76,7 +76,8 @@ class MangaMutation {
// try to initialize uninitialized in library manga to ensure that the expected data is available (chapter list, metadata, ...)
val mangas =
transaction {
MangaTable.select { (MangaTable.id inList ids) and (MangaTable.initialized eq false) }
MangaTable
.select { (MangaTable.id inList ids) and (MangaTable.initialized eq false) }
.map { MangaTable.toDataClass(it) }
}
@@ -198,7 +199,8 @@ class MangaMutation {
val (meta, manga) =
transaction {
val meta =
MangaMetaTable.select { (MangaMetaTable.ref eq mangaId) and (MangaMetaTable.key eq key) }
MangaMetaTable
.select { (MangaMetaTable.ref eq mangaId) and (MangaMetaTable.key eq key) }
.firstOrNull()
MangaMetaTable.deleteWhere { (MangaMetaTable.ref eq mangaId) and (MangaMetaTable.key eq key) }
@@ -48,7 +48,8 @@ class MetaMutation {
val meta =
transaction {
val meta =
GlobalMetaTable.select { GlobalMetaTable.key eq key }
GlobalMetaTable
.select { GlobalMetaTable.key eq key }
.firstOrNull()
GlobalMetaTable.deleteWhere { GlobalMetaTable.key eq key }
@@ -138,7 +138,9 @@ class SettingsMutation {
return SetSettingsPayload(clientMutationId, SettingsType())
}
data class ResetSettingsInput(val clientMutationId: String? = null)
data class ResetSettingsInput(
val clientMutationId: String? = null,
)
data class ResetSettingsPayload(
val clientMutationId: String?,
@@ -68,14 +68,17 @@ class SourceMutation {
val (meta, source) =
transaction {
val meta =
SourceMetaTable.select { (SourceMetaTable.ref eq sourceId) and (SourceMetaTable.key eq key) }
SourceMetaTable
.select { (SourceMetaTable.ref eq sourceId) and (SourceMetaTable.key eq key) }
.firstOrNull()
SourceMetaTable.deleteWhere { (SourceMetaTable.ref eq sourceId) and (SourceMetaTable.key eq key) }
val source =
transaction {
SourceTable.select { SourceTable.id eq sourceId }.firstOrNull()
SourceTable
.select { SourceTable.id eq sourceId }
.firstOrNull()
?.let { SourceType(it) }
}
@@ -139,7 +142,8 @@ class SourceMutation {
val mangas =
transaction {
MangaTable.select { MangaTable.id inList mangaIds }
MangaTable
.select { MangaTable.id inList mangaIds }
.map { MangaType(it) }
}.sortedBy {
mangaIds.indexOf(it.id)
@@ -126,9 +126,10 @@ class TrackMutation {
)
val trackRecord =
transaction {
TrackRecordTable.select {
TrackRecordTable.mangaId eq mangaId and (TrackRecordTable.trackerId eq trackerId)
}.first()
TrackRecordTable
.select {
TrackRecordTable.mangaId eq mangaId and (TrackRecordTable.trackerId eq trackerId)
}.first()
}
BindTrackPayload(
clientMutationId,
@@ -154,9 +155,10 @@ class TrackMutation {
Track.refresh(recordId)
val trackRecord =
transaction {
TrackRecordTable.select {
TrackRecordTable.id eq recordId
}.first()
TrackRecordTable
.select {
TrackRecordTable.id eq recordId
}.first()
}
FetchTrackPayload(
clientMutationId,
@@ -184,9 +186,10 @@ class TrackMutation {
Track.unbind(recordId, deleteRemoteTrack)
val trackRecord =
transaction {
TrackRecordTable.select {
TrackRecordTable.id eq recordId
}.firstOrNull()
TrackRecordTable
.select {
TrackRecordTable.id eq recordId
}.firstOrNull()
}
UnbindTrackPayload(
clientMutationId,
@@ -213,7 +216,8 @@ class TrackMutation {
Track.trackChapter(mangaId)
val trackRecords =
transaction {
TrackRecordTable.select { TrackRecordTable.mangaId eq mangaId }
TrackRecordTable
.select { TrackRecordTable.mangaId eq mangaId }
.toList()
}
TrackProgressPayload(
@@ -241,8 +245,8 @@ class TrackMutation {
val trackRecord: TrackRecordType?,
)
fun updateTrack(input: UpdateTrackInput): CompletableFuture<UpdateTrackPayload> {
return future {
fun updateTrack(input: UpdateTrackInput): CompletableFuture<UpdateTrackPayload> =
future {
Track.update(
Track.UpdateInput(
input.recordId,
@@ -257,14 +261,14 @@ class TrackMutation {
val trackRecord =
transaction {
TrackRecordTable.select {
TrackRecordTable.id eq input.recordId
}.firstOrNull()
TrackRecordTable
.select {
TrackRecordTable.id eq input.recordId
}.firstOrNull()
}
UpdateTrackPayload(
input.clientMutationId,
trackRecord?.let { TrackRecordType(it) },
)
}
}
}
@@ -33,7 +33,5 @@ class BackupQuery {
)
}
fun restoreStatus(id: String): BackupRestoreStatus? {
return ProtoBackupImport.getRestoreState(id)?.toStatus()
}
fun restoreStatus(id: String): BackupRestoreStatus? = ProtoBackupImport.getRestoreState(id)?.toStatus()
}
@@ -45,31 +45,29 @@ class CategoryQuery {
fun category(
dataFetchingEnvironment: DataFetchingEnvironment,
id: Int,
): CompletableFuture<CategoryType> {
return dataFetchingEnvironment.getValueFromDataLoader("CategoryDataLoader", id)
}
): CompletableFuture<CategoryType> = dataFetchingEnvironment.getValueFromDataLoader("CategoryDataLoader", id)
enum class CategoryOrderBy(override val column: Column<out Comparable<*>>) : OrderBy<CategoryType> {
enum class CategoryOrderBy(
override val column: Column<out Comparable<*>>,
) : OrderBy<CategoryType> {
ID(CategoryTable.id),
NAME(CategoryTable.name),
ORDER(CategoryTable.order),
;
override fun greater(cursor: Cursor): Op<Boolean> {
return when (this) {
override fun greater(cursor: Cursor): Op<Boolean> =
when (this) {
ID -> CategoryTable.id greater cursor.value.toInt()
NAME -> greaterNotUnique(CategoryTable.name, CategoryTable.id, cursor, String::toString)
ORDER -> greaterNotUnique(CategoryTable.order, CategoryTable.id, cursor, String::toInt)
}
}
override fun less(cursor: Cursor): Op<Boolean> {
return when (this) {
override fun less(cursor: Cursor): Op<Boolean> =
when (this) {
ID -> CategoryTable.id less cursor.value.toInt()
NAME -> lessNotUnique(CategoryTable.name, CategoryTable.id, cursor, String::toString)
ORDER -> lessNotUnique(CategoryTable.order, CategoryTable.id, cursor, String::toInt)
}
}
override fun asCursor(type: CategoryType): Cursor {
val value =
@@ -113,14 +111,13 @@ class CategoryQuery {
override val or: List<CategoryFilter>? = null,
override val not: CategoryFilter? = null,
) : Filter<CategoryFilter> {
override fun getOpList(): List<Op<Boolean>> {
return listOfNotNull(
override fun getOpList(): List<Op<Boolean>> =
listOfNotNull(
andFilterWithCompareEntity(CategoryTable.id, id),
andFilterWithCompare(CategoryTable.order, order),
andFilterWithCompareString(CategoryTable.name, name),
andFilterWithCompare(CategoryTable.isDefault, default),
)
}
}
fun categories(
@@ -54,11 +54,11 @@ class ChapterQuery {
fun chapter(
dataFetchingEnvironment: DataFetchingEnvironment,
id: Int,
): CompletableFuture<ChapterType> {
return dataFetchingEnvironment.getValueFromDataLoader("ChapterDataLoader", id)
}
): CompletableFuture<ChapterType> = dataFetchingEnvironment.getValueFromDataLoader("ChapterDataLoader", id)
enum class ChapterOrderBy(override val column: Column<out Comparable<*>>) : OrderBy<ChapterType> {
enum class ChapterOrderBy(
override val column: Column<out Comparable<*>>,
) : OrderBy<ChapterType> {
ID(ChapterTable.id),
SOURCE_ORDER(ChapterTable.sourceOrder),
NAME(ChapterTable.name),
@@ -68,8 +68,8 @@ class ChapterQuery {
FETCHED_AT(ChapterTable.fetchedAt),
;
override fun greater(cursor: Cursor): Op<Boolean> {
return when (this) {
override fun greater(cursor: Cursor): Op<Boolean> =
when (this) {
ID -> ChapterTable.id greater cursor.value.toInt()
SOURCE_ORDER -> greaterNotUnique(ChapterTable.sourceOrder, ChapterTable.id, cursor, String::toInt)
NAME -> greaterNotUnique(ChapterTable.name, ChapterTable.id, cursor, String::toString)
@@ -78,10 +78,9 @@ class ChapterQuery {
LAST_READ_AT -> greaterNotUnique(ChapterTable.lastReadAt, ChapterTable.id, cursor, String::toLong)
FETCHED_AT -> greaterNotUnique(ChapterTable.fetchedAt, ChapterTable.id, cursor, String::toLong)
}
}
override fun less(cursor: Cursor): Op<Boolean> {
return when (this) {
override fun less(cursor: Cursor): Op<Boolean> =
when (this) {
ID -> ChapterTable.id less cursor.value.toInt()
SOURCE_ORDER -> lessNotUnique(ChapterTable.sourceOrder, ChapterTable.id, cursor, String::toInt)
NAME -> lessNotUnique(ChapterTable.name, ChapterTable.id, cursor, String::toString)
@@ -90,7 +89,6 @@ class ChapterQuery {
LAST_READ_AT -> lessNotUnique(ChapterTable.lastReadAt, ChapterTable.id, cursor, String::toLong)
FETCHED_AT -> lessNotUnique(ChapterTable.fetchedAt, ChapterTable.id, cursor, String::toLong)
}
}
override fun asCursor(type: ChapterType): Cursor {
val value =
@@ -175,8 +173,8 @@ class ChapterQuery {
override val or: List<ChapterFilter>? = null,
override val not: ChapterFilter? = null,
) : Filter<ChapterFilter> {
override fun getOpList(): List<Op<Boolean>> {
return listOfNotNull(
override fun getOpList(): List<Op<Boolean>> =
listOfNotNull(
andFilterWithCompareEntity(ChapterTable.id, id),
andFilterWithCompareString(ChapterTable.url, url),
andFilterWithCompareString(ChapterTable.name, name),
@@ -194,7 +192,6 @@ class ChapterQuery {
andFilterWithCompare(ChapterTable.isDownloaded, isDownloaded),
andFilterWithCompare(ChapterTable.pageCount, pageCount),
)
}
fun getLibraryOp() = andFilterWithCompare(MangaTable.inLibrary, inLibrary)
}
@@ -7,9 +7,8 @@ import suwayomi.tachidesk.server.JavalinSetup.future
import java.util.concurrent.CompletableFuture
class DownloadQuery {
fun downloadStatus(): CompletableFuture<DownloadStatus> {
return future {
fun downloadStatus(): CompletableFuture<DownloadStatus> =
future {
DownloadStatus(DownloadManager.status.first())
}
}
}
@@ -46,31 +46,29 @@ class ExtensionQuery {
fun extension(
dataFetchingEnvironment: DataFetchingEnvironment,
pkgName: String,
): CompletableFuture<ExtensionType> {
return dataFetchingEnvironment.getValueFromDataLoader("ExtensionDataLoader", pkgName)
}
): CompletableFuture<ExtensionType> = dataFetchingEnvironment.getValueFromDataLoader("ExtensionDataLoader", pkgName)
enum class ExtensionOrderBy(override val column: Column<out Comparable<*>>) : OrderBy<ExtensionType> {
enum class ExtensionOrderBy(
override val column: Column<out Comparable<*>>,
) : OrderBy<ExtensionType> {
PKG_NAME(ExtensionTable.pkgName),
NAME(ExtensionTable.name),
APK_NAME(ExtensionTable.apkName),
;
override fun greater(cursor: Cursor): Op<Boolean> {
return when (this) {
override fun greater(cursor: Cursor): Op<Boolean> =
when (this) {
PKG_NAME -> ExtensionTable.pkgName greater cursor.value
NAME -> greaterNotUnique(ExtensionTable.name, ExtensionTable.pkgName, cursor, String::toString)
APK_NAME -> greaterNotUnique(ExtensionTable.apkName, ExtensionTable.pkgName, cursor, String::toString)
}
}
override fun less(cursor: Cursor): Op<Boolean> {
return when (this) {
override fun less(cursor: Cursor): Op<Boolean> =
when (this) {
PKG_NAME -> ExtensionTable.pkgName less cursor.value
NAME -> lessNotUnique(ExtensionTable.name, ExtensionTable.pkgName, cursor, String::toString)
APK_NAME -> lessNotUnique(ExtensionTable.apkName, ExtensionTable.pkgName, cursor, String::toString)
}
}
override fun asCursor(type: ExtensionType): Cursor {
val value =
@@ -137,8 +135,8 @@ class ExtensionQuery {
override val or: List<ExtensionFilter>? = null,
override val not: ExtensionFilter? = null,
) : Filter<ExtensionFilter> {
override fun getOpList(): List<Op<Boolean>> {
return listOfNotNull(
override fun getOpList(): List<Op<Boolean>> =
listOfNotNull(
andFilterWithCompareString(ExtensionTable.repo, repo),
andFilterWithCompareString(ExtensionTable.apkName, apkName),
andFilterWithCompareString(ExtensionTable.iconUrl, iconUrl),
@@ -152,7 +150,6 @@ class ExtensionQuery {
andFilterWithCompare(ExtensionTable.hasUpdate, hasUpdate),
andFilterWithCompare(ExtensionTable.isObsolete, isObsolete),
)
}
}
fun extensions(
@@ -22,8 +22,8 @@ class InfoQuery {
val discord: String,
)
fun aboutServer(): AboutServerPayload {
return AboutServerPayload(
fun aboutServer(): AboutServerPayload =
AboutServerPayload(
BuildConfig.NAME,
BuildConfig.VERSION,
BuildConfig.REVISION,
@@ -32,7 +32,6 @@ class InfoQuery {
BuildConfig.GITHUB,
BuildConfig.DISCORD,
)
}
data class CheckForServerUpdatesPayload(
/** [channel] mirrors [suwayomi.tachidesk.server.BuildConfig.BUILD_TYPE] */
@@ -41,8 +40,8 @@ class InfoQuery {
val url: String,
)
fun checkForServerUpdates(): CompletableFuture<List<CheckForServerUpdatesPayload>> {
return future {
fun checkForServerUpdates(): CompletableFuture<List<CheckForServerUpdatesPayload>> =
future {
AppUpdate.checkUpdate().map {
CheckForServerUpdatesPayload(
channel = it.channel,
@@ -51,16 +50,14 @@ class InfoQuery {
)
}
}
}
fun aboutWebUI(): CompletableFuture<AboutWebUI> {
return future {
fun aboutWebUI(): CompletableFuture<AboutWebUI> =
future {
WebInterfaceManager.getAboutInfo()
}
}
fun checkForWebUIUpdate(): CompletableFuture<WebUIUpdateCheck> {
return future {
fun checkForWebUIUpdate(): CompletableFuture<WebUIUpdateCheck> =
future {
val (version, updateAvailable) = WebInterfaceManager.isUpdateAvailable(WebUIFlavor.current, raiseError = true)
WebUIUpdateCheck(
channel = serverConfig.webUIChannel.value,
@@ -68,9 +65,6 @@ class InfoQuery {
updateAvailable,
)
}
}
fun getWebUIUpdateStatus(): WebUIUpdateStatus {
return WebInterfaceManager.status.value
}
fun getWebUIUpdateStatus(): WebUIUpdateStatus = WebInterfaceManager.status.value
}
@@ -50,34 +50,32 @@ class MangaQuery {
fun manga(
dataFetchingEnvironment: DataFetchingEnvironment,
id: Int,
): CompletableFuture<MangaType> {
return dataFetchingEnvironment.getValueFromDataLoader("MangaDataLoader", id)
}
): CompletableFuture<MangaType> = dataFetchingEnvironment.getValueFromDataLoader("MangaDataLoader", id)
enum class MangaOrderBy(override val column: Column<out Comparable<*>>) : OrderBy<MangaType> {
enum class MangaOrderBy(
override val column: Column<out Comparable<*>>,
) : OrderBy<MangaType> {
ID(MangaTable.id),
TITLE(MangaTable.title),
IN_LIBRARY_AT(MangaTable.inLibraryAt),
LAST_FETCHED_AT(MangaTable.lastFetchedAt),
;
override fun greater(cursor: Cursor): Op<Boolean> {
return when (this) {
override fun greater(cursor: Cursor): Op<Boolean> =
when (this) {
ID -> MangaTable.id greater cursor.value.toInt()
TITLE -> greaterNotUnique(MangaTable.title, MangaTable.id, cursor, String::toString)
IN_LIBRARY_AT -> greaterNotUnique(MangaTable.inLibraryAt, MangaTable.id, cursor, String::toLong)
LAST_FETCHED_AT -> greaterNotUnique(MangaTable.lastFetchedAt, MangaTable.id, cursor, String::toLong)
}
}
override fun less(cursor: Cursor): Op<Boolean> {
return when (this) {
override fun less(cursor: Cursor): Op<Boolean> =
when (this) {
ID -> MangaTable.id less cursor.value.toInt()
TITLE -> lessNotUnique(MangaTable.title, MangaTable.id, cursor, String::toString)
IN_LIBRARY_AT -> lessNotUnique(MangaTable.inLibraryAt, MangaTable.id, cursor, String::toLong)
LAST_FETCHED_AT -> lessNotUnique(MangaTable.lastFetchedAt, MangaTable.id, cursor, String::toLong)
}
}
override fun asCursor(type: MangaType): Cursor {
val value =
@@ -197,8 +195,8 @@ class MangaQuery {
override val or: List<MangaFilter>? = null,
override val not: MangaFilter? = null,
) : Filter<MangaFilter> {
override fun getOpList(): List<Op<Boolean>> {
return listOfNotNull(
override fun getOpList(): List<Op<Boolean>> =
listOfNotNull(
andFilterWithCompareEntity(MangaTable.id, id),
andFilterWithCompare(MangaTable.sourceReference, sourceId),
andFilterWithCompareString(MangaTable.url, url),
@@ -217,7 +215,6 @@ class MangaQuery {
andFilterWithCompare(MangaTable.chaptersLastFetchedAt, chaptersLastFetchedAt),
andFilterWithCompareEntity(CategoryMangaTable.category, categoryId),
)
}
}
fun mangas(
@@ -243,11 +240,13 @@ class MangaQuery {
val queryResults =
transaction {
val res =
MangaTable.leftJoin(CategoryMangaTable).slice(
distinctOn(MangaTable.id),
*(MangaTable.columns).toTypedArray(),
*(CategoryMangaTable.columns).toTypedArray(),
).selectAll()
MangaTable
.leftJoin(CategoryMangaTable)
.slice(
distinctOn(MangaTable.id),
*(MangaTable.columns).toTypedArray(),
*(CategoryMangaTable.columns).toTypedArray(),
).selectAll()
res.applyOps(condition, filter)
@@ -41,28 +41,26 @@ class MetaQuery {
fun meta(
dataFetchingEnvironment: DataFetchingEnvironment,
key: String,
): CompletableFuture<GlobalMetaType> {
return dataFetchingEnvironment.getValueFromDataLoader("GlobalMetaDataLoader", key)
}
): CompletableFuture<GlobalMetaType> = dataFetchingEnvironment.getValueFromDataLoader("GlobalMetaDataLoader", key)
enum class MetaOrderBy(override val column: Column<out Comparable<*>>) : OrderBy<GlobalMetaType> {
enum class MetaOrderBy(
override val column: Column<out Comparable<*>>,
) : OrderBy<GlobalMetaType> {
KEY(GlobalMetaTable.key),
VALUE(GlobalMetaTable.value),
;
override fun greater(cursor: Cursor): Op<Boolean> {
return when (this) {
override fun greater(cursor: Cursor): Op<Boolean> =
when (this) {
KEY -> GlobalMetaTable.key greater cursor.value
VALUE -> greaterNotUnique(GlobalMetaTable.value, GlobalMetaTable.key, cursor, String::toString)
}
}
override fun less(cursor: Cursor): Op<Boolean> {
return when (this) {
override fun less(cursor: Cursor): Op<Boolean> =
when (this) {
KEY -> GlobalMetaTable.key less cursor.value
VALUE -> lessNotUnique(GlobalMetaTable.value, GlobalMetaTable.key, cursor, String::toString)
}
}
override fun asCursor(type: GlobalMetaType): Cursor {
val value =
@@ -99,12 +97,11 @@ class MetaQuery {
override val or: List<MetaFilter>? = null,
override val not: MetaFilter? = null,
) : Filter<MetaFilter> {
override fun getOpList(): List<Op<Boolean>> {
return listOfNotNull(
override fun getOpList(): List<Op<Boolean>> =
listOfNotNull(
andFilterWithCompareString(GlobalMetaTable.key, key),
andFilterWithCompareString(GlobalMetaTable.value, value),
)
}
}
fun metas(
@@ -3,7 +3,5 @@ package suwayomi.tachidesk.graphql.queries
import suwayomi.tachidesk.graphql.types.SettingsType
class SettingsQuery {
fun settings(): SettingsType {
return SettingsType()
}
fun settings(): SettingsType = SettingsType()
}
@@ -45,31 +45,29 @@ class SourceQuery {
fun source(
dataFetchingEnvironment: DataFetchingEnvironment,
id: Long,
): CompletableFuture<SourceType> {
return dataFetchingEnvironment.getValueFromDataLoader("SourceDataLoader", id)
}
): CompletableFuture<SourceType> = dataFetchingEnvironment.getValueFromDataLoader("SourceDataLoader", id)
enum class SourceOrderBy(override val column: Column<out Comparable<*>>) : OrderBy<SourceType> {
enum class SourceOrderBy(
override val column: Column<out Comparable<*>>,
) : OrderBy<SourceType> {
ID(SourceTable.id),
NAME(SourceTable.name),
LANG(SourceTable.lang),
;
override fun greater(cursor: Cursor): Op<Boolean> {
return when (this) {
override fun greater(cursor: Cursor): Op<Boolean> =
when (this) {
ID -> SourceTable.id greater cursor.value.toLong()
NAME -> greaterNotUnique(SourceTable.name, SourceTable.id, cursor, String::toString)
LANG -> greaterNotUnique(SourceTable.lang, SourceTable.id, cursor, String::toString)
}
}
override fun less(cursor: Cursor): Op<Boolean> {
return when (this) {
override fun less(cursor: Cursor): Op<Boolean> =
when (this) {
ID -> SourceTable.id less cursor.value.toLong()
NAME -> lessNotUnique(SourceTable.name, SourceTable.id, cursor, String::toString)
LANG -> lessNotUnique(SourceTable.lang, SourceTable.id, cursor, String::toString)
}
}
override fun asCursor(type: SourceType): Cursor {
val value =
@@ -113,14 +111,13 @@ class SourceQuery {
override val or: List<SourceFilter>? = null,
override val not: SourceFilter? = null,
) : Filter<SourceFilter> {
override fun getOpList(): List<Op<Boolean>> {
return listOfNotNull(
override fun getOpList(): List<Op<Boolean>> =
listOfNotNull(
andFilterWithCompareEntity(SourceTable.id, id),
andFilterWithCompareString(SourceTable.name, name),
andFilterWithCompareString(SourceTable.lang, lang),
andFilterWithCompare(SourceTable.isNsfw, isNsfw),
)
}
}
fun sources(
@@ -46,9 +46,7 @@ class TrackQuery {
fun tracker(
dataFetchingEnvironment: DataFetchingEnvironment,
id: Int,
): CompletableFuture<TrackerType> {
return dataFetchingEnvironment.getValueFromDataLoader<Int, TrackerType>("TrackerDataLoader", id)
}
): CompletableFuture<TrackerType> = dataFetchingEnvironment.getValueFromDataLoader<Int, TrackerType>("TrackerDataLoader", id)
enum class TrackerOrderBy {
ID,
@@ -59,8 +57,8 @@ class TrackQuery {
fun greater(
tracker: TrackerType,
cursor: Cursor,
): Boolean {
return when (this) {
): Boolean =
when (this) {
ID -> tracker.id > cursor.value.toInt()
NAME -> tracker.name > cursor.value
IS_LOGGED_IN -> {
@@ -68,13 +66,12 @@ class TrackQuery {
!value || tracker.isLoggedIn
}
}
}
fun less(
tracker: TrackerType,
cursor: Cursor,
): Boolean {
return when (this) {
): Boolean =
when (this) {
ID -> tracker.id < cursor.value.toInt()
NAME -> tracker.name < cursor.value
IS_LOGGED_IN -> {
@@ -82,7 +79,6 @@ class TrackQuery {
value || !tracker.isLoggedIn
}
}
}
fun asCursor(type: TrackerType): Cursor {
val value =
@@ -244,11 +240,12 @@ class TrackQuery {
fun trackRecord(
dataFetchingEnvironment: DataFetchingEnvironment,
id: Int,
): CompletableFuture<TrackRecordType> {
return dataFetchingEnvironment.getValueFromDataLoader<Int, TrackRecordType>("TrackRecordDataLoader", id)
}
): CompletableFuture<TrackRecordType> =
dataFetchingEnvironment.getValueFromDataLoader<Int, TrackRecordType>("TrackRecordDataLoader", id)
enum class TrackRecordOrderBy(override val column: Column<out Comparable<*>>) : OrderBy<TrackRecordType> {
enum class TrackRecordOrderBy(
override val column: Column<out Comparable<*>>,
) : OrderBy<TrackRecordType> {
ID(TrackRecordTable.id),
MANGA_ID(TrackRecordTable.mangaId),
TRACKER_ID(TrackRecordTable.trackerId),
@@ -261,8 +258,8 @@ class TrackQuery {
FINISH_DATE(TrackRecordTable.finishDate),
;
override fun greater(cursor: Cursor): Op<Boolean> {
return when (this) {
override fun greater(cursor: Cursor): Op<Boolean> =
when (this) {
ID -> TrackRecordTable.id greater cursor.value.toInt()
MANGA_ID -> greaterNotUnique(TrackRecordTable.mangaId, TrackRecordTable.id, cursor)
TRACKER_ID -> greaterNotUnique(TrackRecordTable.trackerId, TrackRecordTable.id, cursor, String::toInt)
@@ -274,10 +271,9 @@ class TrackQuery {
START_DATE -> greaterNotUnique(TrackRecordTable.startDate, TrackRecordTable.id, cursor, String::toLong)
FINISH_DATE -> greaterNotUnique(TrackRecordTable.finishDate, TrackRecordTable.id, cursor, String::toLong)
}
}
override fun less(cursor: Cursor): Op<Boolean> {
return when (this) {
override fun less(cursor: Cursor): Op<Boolean> =
when (this) {
ID -> TrackRecordTable.id less cursor.value.toInt()
MANGA_ID -> lessNotUnique(TrackRecordTable.mangaId, TrackRecordTable.id, cursor)
TRACKER_ID -> lessNotUnique(TrackRecordTable.trackerId, TrackRecordTable.id, cursor, String::toInt)
@@ -289,7 +285,6 @@ class TrackQuery {
START_DATE -> lessNotUnique(TrackRecordTable.startDate, TrackRecordTable.id, cursor, String::toLong)
FINISH_DATE -> lessNotUnique(TrackRecordTable.finishDate, TrackRecordTable.id, cursor, String::toLong)
}
}
override fun asCursor(type: TrackRecordType): Cursor {
val value =
@@ -367,8 +362,8 @@ class TrackQuery {
override val or: List<TrackRecordFilter>? = null,
override val not: TrackRecordFilter? = null,
) : Filter<TrackRecordFilter> {
override fun getOpList(): List<Op<Boolean>> {
return listOfNotNull(
override fun getOpList(): List<Op<Boolean>> =
listOfNotNull(
andFilterWithCompareEntity(TrackRecordTable.id, id),
andFilterWithCompareEntity(TrackRecordTable.mangaId, mangaId),
andFilterWithCompare(TrackRecordTable.trackerId, trackerId),
@@ -383,7 +378,6 @@ class TrackQuery {
andFilterWithCompare(TrackRecordTable.startDate, startDate),
andFilterWithCompare(TrackRecordTable.finishDate, finishDate),
)
}
}
fun trackRecords(
@@ -484,10 +478,12 @@ class TrackQuery {
val query: String,
)
data class SearchTrackerPayload(val trackSearches: List<TrackSearchType>)
data class SearchTrackerPayload(
val trackSearches: List<TrackSearchType>,
)
fun searchTracker(input: SearchTrackerInput): CompletableFuture<SearchTrackerPayload> {
return future {
fun searchTracker(input: SearchTrackerInput): CompletableFuture<SearchTrackerPayload> =
future {
val tracker =
requireNotNull(TrackerManager.getTracker(input.trackerId)) {
"Tracker not found"
@@ -501,5 +497,4 @@ class TrackQuery {
},
)
}
}
}
@@ -12,13 +12,11 @@ import java.util.concurrent.CompletableFuture
class UpdateQuery {
private val updater by DI.global.instance<IUpdater>()
fun updateStatus(): CompletableFuture<UpdateStatus> {
return future { UpdateStatus(updater.status.first()) }
}
fun updateStatus(): CompletableFuture<UpdateStatus> = future { UpdateStatus(updater.status.first()) }
data class LastUpdateTimestampPayload(val timestamp: Long)
data class LastUpdateTimestampPayload(
val timestamp: Long,
)
fun lastUpdateTimestamp(): LastUpdateTimestampPayload {
return LastUpdateTimestampPayload(updater.getLastUpdateTimestamp())
}
fun lastUpdateTimestamp(): LastUpdateTimestampPayload = LastUpdateTimestampPayload(updater.getLastUpdateTimestamp())
}
@@ -17,11 +17,16 @@ import org.jetbrains.exposed.sql.or
import org.jetbrains.exposed.sql.stringParam
import org.jetbrains.exposed.sql.upperCase
class ILikeEscapeOp(expr1: Expression<*>, expr2: Expression<*>, like: Boolean, val escapeChar: Char?) : ComparisonOp(
expr1,
expr2,
if (like) "ILIKE" else "NOT ILIKE",
) {
class ILikeEscapeOp(
expr1: Expression<*>,
expr2: Expression<*>,
like: Boolean,
val escapeChar: Char?,
) : ComparisonOp(
expr1,
expr2,
if (like) "ILIKE" else "NOT ILIKE",
) {
override fun toQueryBuilder(queryBuilder: QueryBuilder) {
super.toQueryBuilder(queryBuilder)
if (escapeChar != null) {
@@ -67,11 +72,15 @@ class ILikeEscapeOp(expr1: Expression<*>, expr2: Expression<*>, like: Boolean, v
}
}
class DistinctFromOp(expr1: Expression<*>, expr2: Expression<*>, not: Boolean) : ComparisonOp(
expr1,
expr2,
if (not) "IS NOT DISTINCT FROM" else "IS DISTINCT FROM",
) {
class DistinctFromOp(
expr1: Expression<*>,
expr2: Expression<*>,
not: Boolean,
) : ComparisonOp(
expr1,
expr2,
if (not) "IS NOT DISTINCT FROM" else "IS DISTINCT FROM",
) {
companion object {
fun <T> distinctFrom(
expression: ExpressionWithColumnType<T>,
@@ -472,7 +481,9 @@ fun <T : String, S : T?> andFilterWithCompareString(
return opAnd.op
}
class OpAnd(var op: Op<Boolean>? = null) {
class OpAnd(
var op: Op<Boolean>? = null,
) {
fun <T> andWhere(
value: T?,
andPart: SqlExpressionBuilder.(T & Any) -> Op<Boolean>,
@@ -38,26 +38,29 @@ class JavalinGraphQLRequestParser : GraphQLRequestParser<Context> {
GraphQLServerRequest::class.java,
)
val map =
context.formParam("map")?.let {
context.jsonMapper().fromJsonString(
it,
Map::class.java as Class<Map<String, List<String>>>,
)
}.orEmpty()
context
.formParam("map")
?.let {
context.jsonMapper().fromJsonString(
it,
Map::class.java as Class<Map<String, List<String>>>,
)
}.orEmpty()
val mapItems =
map.flatMap { (key, variables) ->
val file = context.uploadedFile(key)
variables.map { fullVariable ->
val variable = fullVariable.removePrefix("variables.").substringBefore('.')
val listIndex = fullVariable.substringAfterLast('.').toIntOrNull()
MapItem(
variable,
listIndex,
file,
)
}
}.groupBy { it.variable }
map
.flatMap { (key, variables) ->
val file = context.uploadedFile(key)
variables.map { fullVariable ->
val variable = fullVariable.removePrefix("variables.").substringBefore('.')
val listIndex = fullVariable.substringAfterLast('.').toIntOrNull()
MapItem(
variable,
listIndex,
file,
)
}
}.groupBy { it.variable }
when (request) {
is GraphQLRequest -> {
@@ -91,8 +94,8 @@ class JavalinGraphQLRequestParser : GraphQLRequestParser<Context> {
* Example map "{ "0": ["variables.file"] }"
* TODO nested objects
*/
private fun Map<String, Any?>.modifyFiles(map: Map<String, List<MapItem>>): Map<String, Any?> {
return mapValues { (name, value) ->
private fun Map<String, Any?>.modifyFiles(map: Map<String, List<MapItem>>): Map<String, Any?> =
mapValues { (name, value) ->
if (map.containsKey(name)) {
val items = map[name].orEmpty()
if (items.size > 1) {
@@ -110,5 +113,4 @@ class JavalinGraphQLRequestParser : GraphQLRequestParser<Context> {
value
}
}
}
}
@@ -46,8 +46,8 @@ import suwayomi.tachidesk.graphql.dataLoaders.UnreadChapterCountForMangaDataLoad
class TachideskDataLoaderRegistryFactory {
companion object {
fun create(): KotlinDataLoaderRegistryFactory {
return KotlinDataLoaderRegistryFactory(
fun create(): KotlinDataLoaderRegistryFactory =
KotlinDataLoaderRegistryFactory(
MangaDataLoader(),
ChapterDataLoader(),
ChaptersForMangaDataLoader(),
@@ -84,6 +84,5 @@ class TachideskDataLoaderRegistryFactory {
TrackRecordsForTrackerIdDataLoader(),
TrackRecordDataLoader(),
)
}
}
}
@@ -34,7 +34,8 @@ class TachideskGraphQLServer(
@OptIn(DelicateCoroutinesApi::class)
fun handleSubscriptionMessage(context: WsMessageContext) {
subscriptionProtocolHandler.handleMessage(context)
subscriptionProtocolHandler
.handleMessage(context)
.map { objectMapper.writeValueAsString(it) }
.map { context.send(it) }
.launchIn(GlobalScope)
@@ -46,7 +47,8 @@ class TachideskGraphQLServer(
companion object {
private fun getGraphQLObject(): GraphQL =
GraphQL.newGraphQL(schema)
GraphQL
.newGraphQL(schema)
.subscriptionExecutionStrategy(FlowSubscriptionExecutionStrategy())
.mutationExecutionStrategy(AsyncExecutionStrategy())
.build()
@@ -43,7 +43,5 @@ object TemporaryFileStorage {
}
}
fun retrieveFile(name: String): Path {
return folder.resolve(name)
}
fun retrieveFile(name: String): Path = folder.resolve(name)
}
@@ -12,18 +12,21 @@ import graphql.schema.CoercingSerializeException
import graphql.schema.GraphQLScalarType
import java.util.Locale
data class Cursor(val value: String)
data class Cursor(
val value: String,
)
val GraphQLCursor: GraphQLScalarType =
GraphQLScalarType.newScalar()
GraphQLScalarType
.newScalar()
.name(
"Cursor",
).description("A location in a connection that can be used for resuming pagination.").coercing(GraphqlCursorCoercing()).build()
).description("A location in a connection that can be used for resuming pagination.")
.coercing(GraphqlCursorCoercing())
.build()
private class GraphqlCursorCoercing : Coercing<Cursor, String> {
private fun toStringImpl(input: Any): String? {
return (input as? Cursor)?.value
}
private fun toStringImpl(input: Any): String? = (input as? Cursor)?.value
private fun parseValueImpl(
input: Any,
@@ -58,54 +61,44 @@ private class GraphqlCursorCoercing : Coercing<Cursor, String> {
return Cursor(input.value)
}
private fun valueToLiteralImpl(input: Any): StringValue {
return StringValue.newStringValue(input.toString()).build()
}
private fun valueToLiteralImpl(input: Any): StringValue = StringValue.newStringValue(input.toString()).build()
@Deprecated("")
override fun serialize(dataFetcherResult: Any): String {
return toStringImpl(dataFetcherResult) ?: throw CoercingSerializeException(
override fun serialize(dataFetcherResult: Any): String =
toStringImpl(dataFetcherResult) ?: throw CoercingSerializeException(
CoercingUtil.i18nMsg(
Locale.getDefault(),
"String.unexpectedRawValueType",
CoercingUtil.typeName(dataFetcherResult),
),
)
}
@Throws(CoercingSerializeException::class)
override fun serialize(
dataFetcherResult: Any,
graphQLContext: GraphQLContext,
locale: Locale,
): String {
return toStringImpl(dataFetcherResult) ?: throw CoercingSerializeException(
): String =
toStringImpl(dataFetcherResult) ?: throw CoercingSerializeException(
CoercingUtil.i18nMsg(
locale,
"String.unexpectedRawValueType",
CoercingUtil.typeName(dataFetcherResult),
),
)
}
@Deprecated("")
override fun parseValue(input: Any): Cursor {
return parseValueImpl(input, Locale.getDefault())
}
override fun parseValue(input: Any): Cursor = parseValueImpl(input, Locale.getDefault())
@Throws(CoercingParseValueException::class)
override fun parseValue(
input: Any,
graphQLContext: GraphQLContext,
locale: Locale,
): Cursor {
return parseValueImpl(input, locale)
}
): Cursor = parseValueImpl(input, locale)
@Deprecated("")
override fun parseLiteral(input: Any): Cursor {
return parseLiteralImpl(input, Locale.getDefault())
}
override fun parseLiteral(input: Any): Cursor = parseLiteralImpl(input, Locale.getDefault())
@Throws(CoercingParseLiteralException::class)
override fun parseLiteral(
@@ -113,20 +106,14 @@ private class GraphqlCursorCoercing : Coercing<Cursor, String> {
variables: CoercedVariables,
graphQLContext: GraphQLContext,
locale: Locale,
): Cursor {
return parseLiteralImpl(input, locale)
}
): Cursor = parseLiteralImpl(input, locale)
@Deprecated("")
override fun valueToLiteral(input: Any): Value<*> {
return valueToLiteralImpl(input)
}
override fun valueToLiteral(input: Any): Value<*> = valueToLiteralImpl(input)
override fun valueToLiteral(
input: Any,
graphQLContext: GraphQLContext,
locale: Locale,
): Value<*> {
return valueToLiteralImpl(input)
}
): Value<*> = valueToLiteralImpl(input)
}
@@ -13,13 +13,15 @@ import graphql.schema.GraphQLScalarType
import java.util.Locale
val GraphQLLongAsString: GraphQLScalarType =
GraphQLScalarType.newScalar()
.name("LongString").description("A 64-bit signed integer as a String").coercing(GraphqlLongAsStringCoercing()).build()
GraphQLScalarType
.newScalar()
.name("LongString")
.description("A 64-bit signed integer as a String")
.coercing(GraphqlLongAsStringCoercing())
.build()
private class GraphqlLongAsStringCoercing : Coercing<Long, String> {
private fun toStringImpl(input: Any): String {
return input.toString()
}
private fun toStringImpl(input: Any): String = input.toString()
private fun parseValueImpl(
input: Any,
@@ -54,42 +56,30 @@ private class GraphqlLongAsStringCoercing : Coercing<Long, String> {
return input.value.toLong()
}
private fun valueToLiteralImpl(input: Any): StringValue {
return StringValue.newStringValue(input.toString()).build()
}
private fun valueToLiteralImpl(input: Any): StringValue = StringValue.newStringValue(input.toString()).build()
@Deprecated("")
override fun serialize(dataFetcherResult: Any): String {
return toStringImpl(dataFetcherResult)
}
override fun serialize(dataFetcherResult: Any): String = toStringImpl(dataFetcherResult)
@Throws(CoercingSerializeException::class)
override fun serialize(
dataFetcherResult: Any,
graphQLContext: GraphQLContext,
locale: Locale,
): String {
return toStringImpl(dataFetcherResult)
}
): String = toStringImpl(dataFetcherResult)
@Deprecated("")
override fun parseValue(input: Any): Long {
return parseValueImpl(input, Locale.getDefault())
}
override fun parseValue(input: Any): Long = parseValueImpl(input, Locale.getDefault())
@Throws(CoercingParseValueException::class)
override fun parseValue(
input: Any,
graphQLContext: GraphQLContext,
locale: Locale,
): Long {
return parseValueImpl(input, locale)
}
): Long = parseValueImpl(input, locale)
@Deprecated("")
override fun parseLiteral(input: Any): Long {
return parseLiteralImpl(input, Locale.getDefault())
}
override fun parseLiteral(input: Any): Long = parseLiteralImpl(input, Locale.getDefault())
@Throws(CoercingParseLiteralException::class)
override fun parseLiteral(
@@ -97,20 +87,14 @@ private class GraphqlLongAsStringCoercing : Coercing<Long, String> {
variables: CoercedVariables,
graphQLContext: GraphQLContext,
locale: Locale,
): Long {
return parseLiteralImpl(input, locale)
}
): Long = parseLiteralImpl(input, locale)
@Deprecated("")
override fun valueToLiteral(input: Any): Value<*> {
return valueToLiteralImpl(input)
}
override fun valueToLiteral(input: Any): Value<*> = valueToLiteralImpl(input)
override fun valueToLiteral(
input: Any,
graphQLContext: GraphQLContext,
locale: Locale,
): Value<*> {
return valueToLiteralImpl(input)
}
): Value<*> = valueToLiteralImpl(input)
}
@@ -27,8 +27,8 @@ interface Order<By : OrderBy<*>> {
val byType: SortOrder?
}
fun SortOrder?.maybeSwap(value: Any?): SortOrder {
return if (value != null) {
fun SortOrder?.maybeSwap(value: Any?): SortOrder =
if (value != null) {
when (this) {
SortOrder.ASC -> SortOrder.DESC
SortOrder.DESC -> SortOrder.ASC
@@ -41,7 +41,6 @@ fun SortOrder?.maybeSwap(value: Any?): SortOrder {
} else {
this ?: SortOrder.ASC
}
}
fun <T> Query.applyBeforeAfter(
before: Cursor?,
@@ -72,9 +71,7 @@ fun <T : Comparable<T>> greaterNotUnique(
idColumn: Column<EntityID<Int>>,
cursor: Cursor,
toValue: (String) -> T,
): Op<Boolean> {
return greaterNotUniqueImpl(column, idColumn, cursor, String::toInt, toValue)
}
): Op<Boolean> = greaterNotUniqueImpl(column, idColumn, cursor, String::toInt, toValue)
@JvmName("greaterNotUniqueLongKey")
fun <T : Comparable<T>> greaterNotUnique(
@@ -82,18 +79,14 @@ fun <T : Comparable<T>> greaterNotUnique(
idColumn: Column<EntityID<Long>>,
cursor: Cursor,
toValue: (String) -> T,
): Op<Boolean> {
return greaterNotUniqueImpl(column, idColumn, cursor, String::toLong, toValue)
}
): Op<Boolean> = greaterNotUniqueImpl(column, idColumn, cursor, String::toLong, toValue)
@JvmName("greaterNotUniqueIntKeyIntValue")
fun greaterNotUnique(
column: Column<EntityID<Int>>,
idColumn: Column<EntityID<Int>>,
cursor: Cursor,
): Op<Boolean> {
return greaterNotUniqueImpl(column, idColumn, cursor, String::toInt, String::toInt)
}
): Op<Boolean> = greaterNotUniqueImpl(column, idColumn, cursor, String::toInt, String::toInt)
private fun <K : Comparable<K>, V : Comparable<V>> greaterNotUniqueImpl(
column: Column<V>,
@@ -138,9 +131,7 @@ fun <T : Comparable<T>> lessNotUnique(
idColumn: Column<EntityID<Int>>,
cursor: Cursor,
toValue: (String) -> T,
): Op<Boolean> {
return lessNotUniqueImpl(column, idColumn, cursor, String::toInt, toValue)
}
): Op<Boolean> = lessNotUniqueImpl(column, idColumn, cursor, String::toInt, toValue)
@JvmName("lessNotUniqueLongKey")
fun <T : Comparable<T>> lessNotUnique(
@@ -148,18 +139,14 @@ fun <T : Comparable<T>> lessNotUnique(
idColumn: Column<EntityID<Long>>,
cursor: Cursor,
toValue: (String) -> T,
): Op<Boolean> {
return lessNotUniqueImpl(column, idColumn, cursor, String::toLong, toValue)
}
): Op<Boolean> = lessNotUniqueImpl(column, idColumn, cursor, String::toLong, toValue)
@JvmName("lessNotUniqueIntKeyIntValue")
fun lessNotUnique(
column: Column<EntityID<Int>>,
idColumn: Column<EntityID<Int>>,
cursor: Cursor,
): Op<Boolean> {
return lessNotUniqueImpl(column, idColumn, cursor, String::toInt, String::toInt)
}
): Op<Boolean> = lessNotUniqueImpl(column, idColumn, cursor, String::toInt, String::toInt)
private fun <K : Comparable<K>, V : Comparable<V>> lessNotUniqueImpl(
column: Column<V>,
@@ -2,4 +2,9 @@ package suwayomi.tachidesk.graphql.server.primitives
import org.jetbrains.exposed.sql.ResultRow
data class QueryResults<T>(val total: Long, val firstKey: T, val lastKey: T, val results: List<ResultRow>)
data class QueryResults<T>(
val total: Long,
val firstKey: T,
val lastKey: T,
val results: List<ResultRow>,
)
@@ -10,7 +10,8 @@ import io.javalin.http.UploadedFile
import java.util.Locale
val GraphQLUpload =
GraphQLScalarType.newScalar()
GraphQLScalarType
.newScalar()
.name("Upload")
.description("A file part in a multipart request")
.coercing(GraphqlUploadCoercing())
@@ -34,35 +35,25 @@ private class GraphqlUploadCoercing : Coercing<UploadedFile, Void?> {
}
@Deprecated("")
override fun serialize(dataFetcherResult: Any): Void? {
throw CoercingSerializeException("Upload is an input-only type")
}
override fun serialize(dataFetcherResult: Any): Void? = throw CoercingSerializeException("Upload is an input-only type")
@Throws(CoercingSerializeException::class)
override fun serialize(
dataFetcherResult: Any,
graphQLContext: GraphQLContext,
locale: Locale,
): Void? {
throw CoercingSerializeException("Upload is an input-only type")
}
): Void? = throw CoercingSerializeException("Upload is an input-only type")
@Deprecated("")
override fun parseValue(input: Any): UploadedFile {
return parseValueImpl(input, Locale.getDefault())
}
override fun parseValue(input: Any): UploadedFile = parseValueImpl(input, Locale.getDefault())
@Throws(CoercingParseValueException::class)
override fun parseValue(
input: Any,
graphQLContext: GraphQLContext,
locale: Locale,
): UploadedFile {
return parseValueImpl(input, locale)
}
): UploadedFile = parseValueImpl(input, locale)
@Deprecated("")
override fun parseLiteral(input: Any): UploadedFile {
return parseValueImpl(input, Locale.getDefault())
}
override fun parseLiteral(input: Any): UploadedFile = parseValueImpl(input, Locale.getDefault())
}
@@ -99,14 +99,13 @@ class ApolloSubscriptionProtocolHandler(
onDisconnect(context)
}
private fun convertToMessageOrNull(payload: String): SubscriptionOperationMessage? {
return try {
private fun convertToMessageOrNull(payload: String): SubscriptionOperationMessage? =
try {
objectMapper.readValue(payload)
} catch (exception: Exception) {
logger.error("Error parsing the subscription message", exception)
null
}
}
private fun startSubscription(
operationMessage: SubscriptionOperationMessage,
@@ -133,15 +132,15 @@ class ApolloSubscriptionProtocolHandler(
try {
val request = objectMapper.convertValue<GraphQLRequest>(payload)
return subscriptionHandler.executeSubscription(request, graphQLContext)
return subscriptionHandler
.executeSubscription(request, graphQLContext)
.map {
if (it.errors?.isNotEmpty() == true) {
SubscriptionOperationMessage(type = GQL_ERROR.type, id = operationMessage.id, payload = it.errors)
} else {
SubscriptionOperationMessage(type = GQL_NEXT.type, id = operationMessage.id, payload = it)
}
}
.onCompletion { if (it == null) emitAll(onComplete(operationMessage)) }
}.onCompletion { if (it == null) emitAll(onComplete(operationMessage)) }
.onStart { sessionState.saveOperation(context, operationMessage, currentCoroutineContext().job) }
} catch (exception: Exception) {
logger.error("Error running graphql subscription", exception)
@@ -169,13 +168,10 @@ class ApolloSubscriptionProtocolHandler(
/**
* Called with the publisher has completed on its own.
*/
private fun onComplete(operationMessage: SubscriptionOperationMessage): Flow<SubscriptionOperationMessage> {
return sessionState.completeOperation(operationMessage)
}
private fun onComplete(operationMessage: SubscriptionOperationMessage): Flow<SubscriptionOperationMessage> =
sessionState.completeOperation(operationMessage)
private fun onPing(): Flow<SubscriptionOperationMessage> {
return flowOf(pongMessage)
}
private fun onPing(): Flow<SubscriptionOperationMessage> = flowOf(pongMessage)
private fun onDisconnect(context: WsContext): Flow<SubscriptionOperationMessage> {
logger.debug("Session \"${context.sessionId}\" disconnected")
@@ -71,9 +71,7 @@ internal class ApolloSubscriptionSessionState {
.onCompletion { removeActiveOperation(operationMessage.id ?: return@onCompletion) }
}
private fun getCompleteMessage(): Flow<SubscriptionOperationMessage> {
return emptyFlow()
}
private fun getCompleteMessage(): Flow<SubscriptionOperationMessage> = emptyFlow()
/**
* Remove active running subscription from the cache and cancel if needed
@@ -23,18 +23,24 @@ data class SubscriptionOperationMessage(
val id: String? = null,
val payload: Any? = null,
) {
enum class CommonMessages(val type: String) {
enum class CommonMessages(
val type: String,
) {
GQL_PING("ping"),
GQL_PONG("pong"),
GQL_COMPLETE("complete"),
}
enum class ClientMessages(val type: String) {
enum class ClientMessages(
val type: String,
) {
GQL_CONNECTION_INIT("connection_init"),
GQL_SUBSCRIBE("subscribe"),
}
enum class ServerMessages(val type: String) {
enum class ServerMessages(
val type: String,
) {
GQL_CONNECTION_ACK("connection_ack"),
GQL_NEXT("next"),
GQL_ERROR("error"),
@@ -13,9 +13,8 @@ import suwayomi.tachidesk.graphql.types.DownloadStatus
import suwayomi.tachidesk.manga.impl.download.DownloadManager
class DownloadSubscription {
fun downloadChanged(): Flow<DownloadStatus> {
return DownloadManager.status.map { downloadStatus ->
fun downloadChanged(): Flow<DownloadStatus> =
DownloadManager.status.map { downloadStatus ->
DownloadStatus(downloadStatus)
}
}
}
@@ -5,7 +5,5 @@ import suwayomi.tachidesk.graphql.types.WebUIUpdateStatus
import suwayomi.tachidesk.server.util.WebInterfaceManager
class InfoSubscription {
fun webUIUpdateStatusChange(): Flow<WebUIUpdateStatus> {
return WebInterfaceManager.status
}
fun webUIUpdateStatusChange(): Flow<WebUIUpdateStatus> = WebInterfaceManager.status
}
@@ -18,9 +18,8 @@ import suwayomi.tachidesk.manga.impl.update.IUpdater
class UpdateSubscription {
private val updater by DI.global.instance<IUpdater>()
fun updateStatusChanged(): Flow<UpdateStatus> {
return updater.status.map { updateStatus ->
fun updateStatusChanged(): Flow<UpdateStatus> =
updater.status.map { updateStatus ->
UpdateStatus(updateStatus)
}
}
}
@@ -16,8 +16,8 @@ data class BackupRestoreStatus(
val mangaProgress: Int,
)
fun ProtoBackupImport.BackupRestoreState.toStatus(): BackupRestoreStatus {
return when (this) {
fun ProtoBackupImport.BackupRestoreState.toStatus(): BackupRestoreStatus =
when (this) {
ProtoBackupImport.BackupRestoreState.Idle ->
BackupRestoreStatus(
state = BackupRestoreState.IDLE,
@@ -49,4 +49,3 @@ fun ProtoBackupImport.BackupRestoreState.toStatus(): BackupRestoreStatus {
mangaProgress = current,
)
}
}
@@ -36,13 +36,11 @@ class CategoryType(
IncludeOrExclude.fromValue(row[CategoryTable.includeInDownload]),
)
fun mangas(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<MangaNodeList> {
return dataFetchingEnvironment.getValueFromDataLoader<Int, MangaNodeList>("MangaForCategoryDataLoader", id)
}
fun mangas(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<MangaNodeList> =
dataFetchingEnvironment.getValueFromDataLoader<Int, MangaNodeList>("MangaForCategoryDataLoader", id)
fun meta(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<List<CategoryMetaType>> {
return dataFetchingEnvironment.getValueFromDataLoader<Int, List<CategoryMetaType>>("CategoryMetaDataLoader", id)
}
fun meta(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<List<CategoryMetaType>> =
dataFetchingEnvironment.getValueFromDataLoader<Int, List<CategoryMetaType>>("CategoryMetaDataLoader", id)
}
data class CategoryNodeList(
@@ -57,8 +55,8 @@ data class CategoryNodeList(
) : Edge()
companion object {
fun List<CategoryType>.toNodeList(): CategoryNodeList {
return CategoryNodeList(
fun List<CategoryType>.toNodeList(): CategoryNodeList =
CategoryNodeList(
nodes = this,
edges = getEdges(),
pageInfo =
@@ -70,7 +68,6 @@ data class CategoryNodeList(
),
totalCount = size,
)
}
private fun List<CategoryType>.getEdges(): List<CategoryEdge> {
if (isEmpty()) return emptyList()
@@ -90,13 +90,11 @@ class ChapterType(
dataClass.pageCount,
)
fun manga(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<MangaType> {
return dataFetchingEnvironment.getValueFromDataLoader<Int, MangaType>("MangaDataLoader", mangaId)
}
fun manga(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<MangaType> =
dataFetchingEnvironment.getValueFromDataLoader<Int, MangaType>("MangaDataLoader", mangaId)
fun meta(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<List<ChapterMetaType>> {
return dataFetchingEnvironment.getValueFromDataLoader<Int, List<ChapterMetaType>>("ChapterMetaDataLoader", id)
}
fun meta(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<List<ChapterMetaType>> =
dataFetchingEnvironment.getValueFromDataLoader<Int, List<ChapterMetaType>>("ChapterMetaDataLoader", id)
}
data class ChapterNodeList(
@@ -111,8 +109,8 @@ data class ChapterNodeList(
) : Edge()
companion object {
fun List<ChapterType>.toNodeList(): ChapterNodeList {
return ChapterNodeList(
fun List<ChapterType>.toNodeList(): ChapterNodeList =
ChapterNodeList(
nodes = this,
edges = getEdges(),
pageInfo =
@@ -124,7 +122,6 @@ data class ChapterNodeList(
),
totalCount = size,
)
}
private fun List<ChapterType>.getEdges(): List<ChapterEdge> {
if (isEmpty()) return emptyList()
@@ -100,8 +100,8 @@ data class DownloadNodeList(
) : Edge()
companion object {
fun List<DownloadType>.toNodeList(): DownloadNodeList {
return DownloadNodeList(
fun List<DownloadType>.toNodeList(): DownloadNodeList =
DownloadNodeList(
nodes = this,
edges = getEdges(),
pageInfo =
@@ -113,7 +113,6 @@ data class DownloadNodeList(
),
totalCount = size,
)
}
private fun List<DownloadType>.getEdges(): List<DownloadEdge> {
if (isEmpty()) return emptyList()
@@ -48,9 +48,8 @@ class ExtensionType(
isObsolete = row[ExtensionTable.isObsolete],
)
fun source(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<SourceNodeList> {
return dataFetchingEnvironment.getValueFromDataLoader<String, SourceNodeList>("SourcesForExtensionDataLoader", pkgName)
}
fun source(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<SourceNodeList> =
dataFetchingEnvironment.getValueFromDataLoader<String, SourceNodeList>("SourcesForExtensionDataLoader", pkgName)
}
data class ExtensionNodeList(
@@ -65,8 +64,8 @@ data class ExtensionNodeList(
) : Edge()
companion object {
fun List<ExtensionType>.toNodeList(): ExtensionNodeList {
return ExtensionNodeList(
fun List<ExtensionType>.toNodeList(): ExtensionNodeList =
ExtensionNodeList(
nodes = this,
edges = getEdges(),
pageInfo =
@@ -78,7 +77,6 @@ data class ExtensionNodeList(
),
totalCount = size,
)
}
private fun List<ExtensionType>.getEdges(): List<ExtensionEdge> {
if (isEmpty()) return emptyList()
@@ -62,8 +62,10 @@ class MangaType(
val mangaForIdsDataLoader =
dataFetchingEnvironment.getDataLoader<List<Int>, MangaNodeList>("MangaForIdsDataLoader")
@Suppress("UNCHECKED_CAST")
(mangaForIdsDataLoader.cacheMap as CustomCacheMap<List<Int>, MangaNodeList>).getKeys()
.filter { it.contains(mangaId) }.forEach { mangaForIdsDataLoader.clear(it) }
(mangaForIdsDataLoader.cacheMap as CustomCacheMap<List<Int>, MangaNodeList>)
.getKeys()
.filter { it.contains(mangaId) }
.forEach { mangaForIdsDataLoader.clear(it) }
dataFetchingEnvironment.getDataLoader<Int, Int>("DownloadedChapterCountForMangaDataLoader").clear(mangaId)
dataFetchingEnvironment.getDataLoader<Int, Int>("UnreadChapterCountForMangaDataLoader").clear(mangaId)
@@ -74,9 +76,10 @@ class MangaType(
dataFetchingEnvironment.getDataLoader<Int, ChapterType>("LatestFetchedChapterForMangaDataLoader").clear(mangaId)
dataFetchingEnvironment.getDataLoader<Int, ChapterType>("LatestUploadedChapterForMangaDataLoader").clear(mangaId)
dataFetchingEnvironment.getDataLoader<Int, ChapterType>("FirstUnreadChapterForMangaDataLoader").clear(mangaId)
dataFetchingEnvironment.getDataLoader<Int, ChapterNodeList>(
"ChaptersForMangaDataLoader",
).clear(mangaId)
dataFetchingEnvironment
.getDataLoader<Int, ChapterNodeList>(
"ChaptersForMangaDataLoader",
).clear(mangaId)
dataFetchingEnvironment.getDataLoader<Int, List<MangaMetaType>>("MangaMetaDataLoader").clear(mangaId)
dataFetchingEnvironment.getDataLoader<Int, CategoryNodeList>("CategoriesForMangaDataLoader").clear(mangaId)
}
@@ -124,45 +127,35 @@ class MangaType(
dataClass.chaptersLastFetchedAt,
)
fun downloadCount(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<Int> {
return dataFetchingEnvironment.getValueFromDataLoader("DownloadedChapterCountForMangaDataLoader", id)
}
fun downloadCount(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<Int> =
dataFetchingEnvironment.getValueFromDataLoader("DownloadedChapterCountForMangaDataLoader", id)
fun unreadCount(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<Int> {
return dataFetchingEnvironment.getValueFromDataLoader("UnreadChapterCountForMangaDataLoader", id)
}
fun unreadCount(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<Int> =
dataFetchingEnvironment.getValueFromDataLoader("UnreadChapterCountForMangaDataLoader", id)
fun bookmarkCount(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<Int> {
return dataFetchingEnvironment.getValueFromDataLoader("BookmarkedChapterCountForMangaDataLoader", id)
}
fun bookmarkCount(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<Int> =
dataFetchingEnvironment.getValueFromDataLoader("BookmarkedChapterCountForMangaDataLoader", id)
fun hasDuplicateChapters(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<Boolean> {
return dataFetchingEnvironment.getValueFromDataLoader("HasDuplicateChaptersForMangaDataLoader", id)
}
fun hasDuplicateChapters(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<Boolean> =
dataFetchingEnvironment.getValueFromDataLoader("HasDuplicateChaptersForMangaDataLoader", id)
fun lastReadChapter(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<ChapterType?> {
return dataFetchingEnvironment.getValueFromDataLoader("LastReadChapterForMangaDataLoader", id)
}
fun lastReadChapter(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<ChapterType?> =
dataFetchingEnvironment.getValueFromDataLoader("LastReadChapterForMangaDataLoader", id)
fun latestReadChapter(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<ChapterType?> {
return dataFetchingEnvironment.getValueFromDataLoader("LatestReadChapterForMangaDataLoader", id)
}
fun latestReadChapter(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<ChapterType?> =
dataFetchingEnvironment.getValueFromDataLoader("LatestReadChapterForMangaDataLoader", id)
fun latestFetchedChapter(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<ChapterType?> {
return dataFetchingEnvironment.getValueFromDataLoader("LatestFetchedChapterForMangaDataLoader", id)
}
fun latestFetchedChapter(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<ChapterType?> =
dataFetchingEnvironment.getValueFromDataLoader("LatestFetchedChapterForMangaDataLoader", id)
fun latestUploadedChapter(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<ChapterType?> {
return dataFetchingEnvironment.getValueFromDataLoader("LatestUploadedChapterForMangaDataLoader", id)
}
fun latestUploadedChapter(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<ChapterType?> =
dataFetchingEnvironment.getValueFromDataLoader("LatestUploadedChapterForMangaDataLoader", id)
fun firstUnreadChapter(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<ChapterType?> {
return dataFetchingEnvironment.getValueFromDataLoader("FirstUnreadChapterForMangaDataLoader", id)
}
fun firstUnreadChapter(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<ChapterType?> =
dataFetchingEnvironment.getValueFromDataLoader("FirstUnreadChapterForMangaDataLoader", id)
fun chapters(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<ChapterNodeList> {
return dataFetchingEnvironment.getValueFromDataLoader<Int, ChapterNodeList>("ChaptersForMangaDataLoader", id)
}
fun chapters(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<ChapterNodeList> =
dataFetchingEnvironment.getValueFromDataLoader<Int, ChapterNodeList>("ChaptersForMangaDataLoader", id)
fun age(): Long? {
if (lastFetchedAt == null) return null
@@ -175,21 +168,17 @@ class MangaType(
return Instant.now().epochSecond.minus(chaptersLastFetchedAt!!)
}
fun meta(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<List<MangaMetaType>> {
return dataFetchingEnvironment.getValueFromDataLoader<Int, List<MangaMetaType>>("MangaMetaDataLoader", id)
}
fun meta(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<List<MangaMetaType>> =
dataFetchingEnvironment.getValueFromDataLoader<Int, List<MangaMetaType>>("MangaMetaDataLoader", id)
fun categories(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<CategoryNodeList> {
return dataFetchingEnvironment.getValueFromDataLoader<Int, CategoryNodeList>("CategoriesForMangaDataLoader", id)
}
fun categories(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<CategoryNodeList> =
dataFetchingEnvironment.getValueFromDataLoader<Int, CategoryNodeList>("CategoriesForMangaDataLoader", id)
fun source(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<SourceType?> {
return dataFetchingEnvironment.getValueFromDataLoader<Long, SourceType?>("SourceDataLoader", sourceId)
}
fun source(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<SourceType?> =
dataFetchingEnvironment.getValueFromDataLoader<Long, SourceType?>("SourceDataLoader", sourceId)
fun trackRecords(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<TrackRecordNodeList> {
return dataFetchingEnvironment.getValueFromDataLoader<Int, TrackRecordNodeList>("TrackRecordsForMangaIdDataLoader", id)
}
fun trackRecords(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<TrackRecordNodeList> =
dataFetchingEnvironment.getValueFromDataLoader<Int, TrackRecordNodeList>("TrackRecordsForMangaIdDataLoader", id)
}
data class MangaNodeList(
@@ -204,8 +193,8 @@ data class MangaNodeList(
) : Edge()
companion object {
fun List<MangaType>.toNodeList(): MangaNodeList {
return MangaNodeList(
fun List<MangaType>.toNodeList(): MangaNodeList =
MangaNodeList(
nodes = this,
edges = getEdges(),
pageInfo =
@@ -217,7 +206,6 @@ data class MangaNodeList(
),
totalCount = size,
)
}
private fun List<MangaType>.getEdges(): List<MangaEdge> {
if (isEmpty()) return emptyList()
@@ -31,9 +31,8 @@ class ChapterMetaType(
chapterId = row[ChapterMetaTable.ref].value,
)
fun chapter(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<ChapterType> {
return dataFetchingEnvironment.getValueFromDataLoader<Int, ChapterType>("ChapterDataLoader", chapterId)
}
fun chapter(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<ChapterType> =
dataFetchingEnvironment.getValueFromDataLoader<Int, ChapterType>("ChapterDataLoader", chapterId)
}
class MangaMetaType(
@@ -47,9 +46,8 @@ class MangaMetaType(
mangaId = row[MangaMetaTable.ref].value,
)
fun manga(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<MangaType> {
return dataFetchingEnvironment.getValueFromDataLoader<Int, MangaType>("MangaDataLoader", mangaId)
}
fun manga(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<MangaType> =
dataFetchingEnvironment.getValueFromDataLoader<Int, MangaType>("MangaDataLoader", mangaId)
}
class CategoryMetaType(
@@ -63,9 +61,8 @@ class CategoryMetaType(
categoryId = row[CategoryMetaTable.ref].value,
)
fun category(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<CategoryType> {
return dataFetchingEnvironment.getValueFromDataLoader<Int, CategoryType>("CategoryDataLoader", categoryId)
}
fun category(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<CategoryType> =
dataFetchingEnvironment.getValueFromDataLoader<Int, CategoryType>("CategoryDataLoader", categoryId)
}
class SourceMetaType(
@@ -79,9 +76,8 @@ class SourceMetaType(
sourceId = row[SourceMetaTable.ref],
)
fun source(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<SourceType> {
return dataFetchingEnvironment.getValueFromDataLoader<Long, SourceType>("SourceDataLoader", sourceId)
}
fun source(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<SourceType> =
dataFetchingEnvironment.getValueFromDataLoader<Long, SourceType>("SourceDataLoader", sourceId)
}
class GlobalMetaType(
@@ -106,8 +102,8 @@ data class GlobalMetaNodeList(
) : Edge()
companion object {
fun List<GlobalMetaType>.toNodeList(): GlobalMetaNodeList {
return GlobalMetaNodeList(
fun List<GlobalMetaType>.toNodeList(): GlobalMetaNodeList =
GlobalMetaNodeList(
nodes = this,
edges = getEdges(),
pageInfo =
@@ -119,7 +115,6 @@ data class GlobalMetaNodeList(
),
totalCount = size,
)
}
private fun List<GlobalMetaType>.getEdges(): List<MetaEdge> {
if (isEmpty()) return emptyList()
@@ -67,25 +67,18 @@ class SourceType(
displayName = catalogueSource.toString(),
)
fun manga(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<MangaNodeList> {
return dataFetchingEnvironment.getValueFromDataLoader<Long, MangaNodeList>("MangaForSourceDataLoader", id)
}
fun manga(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<MangaNodeList> =
dataFetchingEnvironment.getValueFromDataLoader<Long, MangaNodeList>("MangaForSourceDataLoader", id)
fun extension(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<ExtensionType> {
return dataFetchingEnvironment.getValueFromDataLoader<Long, ExtensionType>("ExtensionForSourceDataLoader", id)
}
fun extension(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<ExtensionType> =
dataFetchingEnvironment.getValueFromDataLoader<Long, ExtensionType>("ExtensionForSourceDataLoader", id)
fun preferences(): List<Preference> {
return getSourcePreferencesRaw(id).map { preferenceOf(it) }
}
fun preferences(): List<Preference> = getSourcePreferencesRaw(id).map { preferenceOf(it) }
fun filters(): List<Filter> {
return getCatalogueSourceOrStub(id).getFilterList().map { filterOf(it) }
}
fun filters(): List<Filter> = getCatalogueSourceOrStub(id).getFilterList().map { filterOf(it) }
fun meta(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<List<SourceMetaType>> {
return dataFetchingEnvironment.getValueFromDataLoader<Long, List<SourceMetaType>>("SourceMetaDataLoader", id)
}
fun meta(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<List<SourceMetaType>> =
dataFetchingEnvironment.getValueFromDataLoader<Long, List<SourceMetaType>>("SourceMetaDataLoader", id)
}
@Suppress("ktlint:standard:function-naming")
@@ -118,8 +111,8 @@ data class SourceNodeList(
) : Edge()
companion object {
fun List<SourceType>.toNodeList(): SourceNodeList {
return SourceNodeList(
fun List<SourceType>.toNodeList(): SourceNodeList =
SourceNodeList(
nodes = this,
edges = getEdges(),
pageInfo =
@@ -131,7 +124,6 @@ data class SourceNodeList(
),
totalCount = size,
)
}
private fun List<SourceType>.getEdges(): List<SourceEdge> {
if (isEmpty()) return emptyList()
@@ -151,15 +143,29 @@ data class SourceNodeList(
sealed interface Filter
data class HeaderFilter(val name: String) : Filter
data class HeaderFilter(
val name: String,
) : Filter
data class SeparatorFilter(val name: String) : Filter
data class SeparatorFilter(
val name: String,
) : Filter
data class SelectFilter(val name: String, val values: List<String>, val default: Int) : Filter
data class SelectFilter(
val name: String,
val values: List<String>,
val default: Int,
) : Filter
data class TextFilter(val name: String, val default: String) : Filter
data class TextFilter(
val name: String,
val default: String,
) : Filter
data class CheckBoxFilter(val name: String, val default: Boolean) : Filter
data class CheckBoxFilter(
val name: String,
val default: Boolean,
) : Filter
enum class TriState {
IGNORE,
@@ -167,19 +173,32 @@ enum class TriState {
EXCLUDE,
}
data class TriStateFilter(val name: String, val default: TriState) : Filter
data class TriStateFilter(
val name: String,
val default: TriState,
) : Filter
data class SortFilter(val name: String, val values: List<String>, val default: SortSelection?) : Filter {
data class SortSelection(val index: Int, val ascending: Boolean) {
data class SortFilter(
val name: String,
val values: List<String>,
val default: SortSelection?,
) : Filter {
data class SortSelection(
val index: Int,
val ascending: Boolean,
) {
constructor(selection: SourceFilter.Sort.Selection) :
this(selection.index, selection.ascending)
}
}
data class GroupFilter(val name: String, val filters: List<Filter>) : Filter
data class GroupFilter(
val name: String,
val filters: List<Filter>,
) : Filter
fun filterOf(filter: SourceFilter<*>): Filter {
return when (filter) {
fun filterOf(filter: SourceFilter<*>): Filter =
when (filter) {
is SourceFilter.Header -> HeaderFilter(filter.name)
is SourceFilter.Separator -> SeparatorFilter(filter.name)
is SourceFilter.Select<*> -> SelectFilter(filter.name, filter.displayValues, filter.state)
@@ -202,7 +221,6 @@ fun filterOf(filter: SourceFilter<*>): Filter {
is SourceFilter.Sort -> SortFilter(filter.name, filter.values.asList(), filter.state?.let(SortFilter::SortSelection))
else -> throw RuntimeException("sealed class cannot have more subtypes!")
}
}
/*sealed interface FilterChange {
val position: Int
@@ -372,8 +390,8 @@ data class MultiSelectListPreference(
val entryValues: List<String>,
) : Preference
fun preferenceOf(preference: SourcePreference): Preference {
return when (preference) {
fun preferenceOf(preference: SourcePreference): Preference =
when (preference) {
is SourceSwitchPreference ->
SwitchPreference(
preference.key,
@@ -430,4 +448,3 @@ fun preferenceOf(preference: SourcePreference): Preference {
)
else -> throw RuntimeException("sealed class cannot have more subtypes!")
}
}
@@ -40,21 +40,17 @@ class TrackerType(
tracker.supportsTrackDeletion,
)
fun statuses(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<List<TrackStatusType>> {
return dataFetchingEnvironment.getValueFromDataLoader<Int, List<TrackStatusType>>("TrackerStatusesDataLoader", id)
}
fun statuses(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<List<TrackStatusType>> =
dataFetchingEnvironment.getValueFromDataLoader<Int, List<TrackStatusType>>("TrackerStatusesDataLoader", id)
fun scores(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<List<String>> {
return dataFetchingEnvironment.getValueFromDataLoader<Int, List<String>>("TrackerScoresDataLoader", id)
}
fun scores(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<List<String>> =
dataFetchingEnvironment.getValueFromDataLoader<Int, List<String>>("TrackerScoresDataLoader", id)
fun trackRecords(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<TrackRecordNodeList> {
return dataFetchingEnvironment.getValueFromDataLoader<Int, TrackRecordNodeList>("TrackRecordsForTrackerIdDataLoader", id)
}
fun trackRecords(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<TrackRecordNodeList> =
dataFetchingEnvironment.getValueFromDataLoader<Int, TrackRecordNodeList>("TrackRecordsForTrackerIdDataLoader", id)
fun isTokenExpired(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<Boolean> {
return dataFetchingEnvironment.getValueFromDataLoader<Int, Boolean>("TrackerTokenExpiredDataLoader", id)
}
fun isTokenExpired(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<Boolean> =
dataFetchingEnvironment.getValueFromDataLoader<Int, Boolean>("TrackerTokenExpiredDataLoader", id)
}
class TrackStatusType(
@@ -93,17 +89,14 @@ class TrackRecordType(
row[TrackRecordTable.finishDate],
)
fun displayScore(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<String> {
return dataFetchingEnvironment.getValueFromDataLoader<Int, String>("DisplayScoreForTrackRecordDataLoader", id)
}
fun displayScore(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<String> =
dataFetchingEnvironment.getValueFromDataLoader<Int, String>("DisplayScoreForTrackRecordDataLoader", id)
fun manga(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<MangaType> {
return dataFetchingEnvironment.getValueFromDataLoader<Int, MangaType>("MangaDataLoader", mangaId)
}
fun manga(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<MangaType> =
dataFetchingEnvironment.getValueFromDataLoader<Int, MangaType>("MangaDataLoader", mangaId)
fun tracker(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<TrackerType> {
return dataFetchingEnvironment.getValueFromDataLoader<Int, TrackerType>("TrackerDataLoader", trackerId)
}
fun tracker(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<TrackerType> =
dataFetchingEnvironment.getValueFromDataLoader<Int, TrackerType>("TrackerDataLoader", trackerId)
}
class TrackSearchType(
@@ -133,9 +126,8 @@ class TrackSearchType(
row[TrackSearchTable.startDate],
)
fun tracker(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<TrackerType> {
return dataFetchingEnvironment.getValueFromDataLoader<Int, TrackerType>("TrackerDataLoader", trackerId)
}
fun tracker(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<TrackerType> =
dataFetchingEnvironment.getValueFromDataLoader<Int, TrackerType>("TrackerDataLoader", trackerId)
}
data class TrackRecordNodeList(
@@ -150,8 +142,8 @@ data class TrackRecordNodeList(
) : Edge()
companion object {
fun List<TrackRecordType>.toNodeList(): TrackRecordNodeList {
return TrackRecordNodeList(
fun List<TrackRecordType>.toNodeList(): TrackRecordNodeList =
TrackRecordNodeList(
nodes = this,
edges = getEdges(),
pageInfo =
@@ -163,7 +155,6 @@ data class TrackRecordNodeList(
),
totalCount = size,
)
}
private fun List<TrackRecordType>.getEdges(): List<TrackRecordEdge> {
if (isEmpty()) return emptyList()
@@ -193,8 +184,8 @@ data class TrackerNodeList(
) : Edge()
companion object {
fun List<TrackerType>.toNodeList(): TrackerNodeList {
return TrackerNodeList(
fun List<TrackerType>.toNodeList(): TrackerNodeList =
TrackerNodeList(
nodes = this,
edges = getEdges(),
pageInfo =
@@ -206,7 +197,6 @@ data class TrackerNodeList(
),
totalCount = size,
)
}
private fun List<TrackerType>.getEdges(): List<TrackerEdge> {
if (isEmpty()) return emptyList()
@@ -28,9 +28,10 @@ class UpdateStatus(
runningJobs = UpdateStatusType(status.mangaStatusMap[JobStatus.RUNNING]?.map { it.id }.orEmpty()),
completeJobs =
UpdateStatusType(
status.mangaStatusMap[JobStatus.COMPLETE]?.map {
it.id
}.orEmpty(),
status.mangaStatusMap[JobStatus.COMPLETE]
?.map {
it.id
}.orEmpty(),
JobStatus.COMPLETE,
status.running,
true,
@@ -50,9 +51,8 @@ class UpdateStatusCategoryType(
@get:GraphQLIgnore
val categoryIds: List<Int>,
) {
fun categories(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<CategoryNodeList> {
return dataFetchingEnvironment.getValueFromDataLoader("CategoryForIdsDataLoader", categoryIds)
}
fun categories(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<CategoryNodeList> =
dataFetchingEnvironment.getValueFromDataLoader("CategoryForIdsDataLoader", categoryIds)
}
class UpdateStatusType(
@@ -247,7 +247,8 @@ object SourceController {
description("All source search")
}
},
behaviorOf = { ctx, searchTerm -> // TODO
behaviorOf = { ctx, searchTerm ->
// TODO
ctx.json(Search.sourceGlobalSearch(searchTerm))
},
withResults = {
@@ -36,10 +36,11 @@ object Category {
return transaction {
if (CategoryTable.select { CategoryTable.name eq name }.firstOrNull() == null) {
val newCategoryId =
CategoryTable.insertAndGetId {
it[CategoryTable.name] = name
it[CategoryTable.order] = Int.MAX_VALUE
}.value
CategoryTable
.insertAndGetId {
it[CategoryTable.name] = name
it[CategoryTable.order] = Int.MAX_VALUE
}.value
normalizeCategories()
@@ -60,7 +61,8 @@ object Category {
transaction {
CategoryTable.update({ CategoryTable.id eq categoryId }) {
if (
categoryId != DEFAULT_CATEGORY_ID && name != null &&
categoryId != DEFAULT_CATEGORY_ID &&
name != null &&
!name.equals(DEFAULT_CATEGORY_NAME, ignoreCase = true)
) {
it[CategoryTable.name] = name
@@ -82,9 +84,11 @@ object Category {
if (from == 0 || to == 0) return
transaction {
val categories =
CategoryTable.select {
CategoryTable.id neq DEFAULT_CATEGORY_ID
}.orderBy(CategoryTable.order to SortOrder.ASC).toMutableList()
CategoryTable
.select {
CategoryTable.id neq DEFAULT_CATEGORY_ID
}.orderBy(CategoryTable.order to SortOrder.ASC)
.toMutableList()
categories.add(to - 1, categories.removeAt(from - 1))
categories.forEachIndexed { index, cat ->
CategoryTable.update({ CategoryTable.id eq cat[CategoryTable.id].value }) {
@@ -106,7 +110,8 @@ object Category {
/** make sure category order numbers starts from 1 and is consecutive */
fun normalizeCategories() {
transaction {
CategoryTable.selectAll()
CategoryTable
.selectAll()
.orderBy(CategoryTable.order to SortOrder.ASC)
.sortedWith(compareBy({ it[CategoryTable.id].value != 0 }, { it[CategoryTable.order] }))
.forEachIndexed { index, cat ->
@@ -130,9 +135,10 @@ object Category {
const val DEFAULT_CATEGORY_ID = 0
const val DEFAULT_CATEGORY_NAME = "Default"
fun getCategoryList(): List<CategoryDataClass> {
return transaction {
CategoryTable.selectAll()
fun getCategoryList(): List<CategoryDataClass> =
transaction {
CategoryTable
.selectAll()
.orderBy(CategoryTable.order to SortOrder.ASC)
.let {
if (needsDefaultCategory()) {
@@ -140,41 +146,39 @@ object Category {
} else {
it.andWhere { CategoryTable.id neq DEFAULT_CATEGORY_ID }
}
}
.map {
}.map {
CategoryTable.toDataClass(it)
}
}
}
fun getCategoryById(categoryId: Int): CategoryDataClass? {
return transaction {
fun getCategoryById(categoryId: Int): CategoryDataClass? =
transaction {
CategoryTable.select { CategoryTable.id eq categoryId }.firstOrNull()?.let {
CategoryTable.toDataClass(it)
}
}
}
fun getCategorySize(categoryId: Int): Int {
return transaction {
fun getCategorySize(categoryId: Int): Int =
transaction {
if (categoryId == DEFAULT_CATEGORY_ID) {
MangaTable
.leftJoin(CategoryMangaTable)
.select { MangaTable.inLibrary eq true }
.andWhere { CategoryMangaTable.manga.isNull() }
} else {
CategoryMangaTable.leftJoin(MangaTable).select { CategoryMangaTable.category eq categoryId }
CategoryMangaTable
.leftJoin(MangaTable)
.select { CategoryMangaTable.category eq categoryId }
.andWhere { MangaTable.inLibrary eq true }
}.count().toInt()
}
}
fun getCategoryMetaMap(categoryId: Int): Map<String, String> {
return transaction {
CategoryMetaTable.select { CategoryMetaTable.ref eq categoryId }
fun getCategoryMetaMap(categoryId: Int): Map<String, String> =
transaction {
CategoryMetaTable
.select { CategoryMetaTable.ref eq categoryId }
.associate { it[CategoryMetaTable.key] to it[CategoryMetaTable.value] }
}
}
fun modifyMeta(
categoryId: Int,
@@ -38,9 +38,10 @@ object CategoryManga {
if (categoryId == DEFAULT_CATEGORY_ID) return
fun notAlreadyInCategory() =
CategoryMangaTable.select {
(CategoryMangaTable.category eq categoryId) and (CategoryMangaTable.manga eq mangaId)
}.isEmpty()
CategoryMangaTable
.select {
(CategoryMangaTable.category eq categoryId) and (CategoryMangaTable.manga eq mangaId)
}.isEmpty()
transaction {
if (notAlreadyInCategory()) {
@@ -69,15 +70,17 @@ object CategoryManga {
// Select the required columns from the MangaTable and add the aggregate functions to compute unread, download, and chapter counts
val unreadCount =
wrapAsExpression<Long>(
ChapterTable.slice(
ChapterTable.id.count(),
).select((ChapterTable.isRead eq false) and (ChapterTable.manga eq MangaTable.id)),
ChapterTable
.slice(
ChapterTable.id.count(),
).select((ChapterTable.isRead eq false) and (ChapterTable.manga eq MangaTable.id)),
)
val downloadedCount =
wrapAsExpression<Long>(
ChapterTable.slice(
ChapterTable.id.count(),
).select((ChapterTable.isDownloaded eq true) and (ChapterTable.manga eq MangaTable.id)),
ChapterTable
.slice(
ChapterTable.id.count(),
).select((ChapterTable.isDownloaded eq true) and (ChapterTable.manga eq MangaTable.id)),
)
val chapterCount = ChapterTable.id.count().alias("chapter_count")
@@ -119,20 +122,23 @@ object CategoryManga {
/**
* list of categories that a manga belongs to
*/
fun getMangaCategories(mangaId: Int): List<CategoryDataClass> {
return transaction {
CategoryMangaTable.innerJoin(CategoryTable).select {
CategoryMangaTable.manga eq mangaId
}.orderBy(CategoryTable.order to SortOrder.ASC).map {
CategoryTable.toDataClass(it)
}
fun getMangaCategories(mangaId: Int): List<CategoryDataClass> =
transaction {
CategoryMangaTable
.innerJoin(CategoryTable)
.select {
CategoryMangaTable.manga eq mangaId
}.orderBy(CategoryTable.order to SortOrder.ASC)
.map {
CategoryTable.toDataClass(it)
}
}
}
fun getMangasCategories(mangaIDs: List<Int>): Map<Int, List<CategoryDataClass>> {
return buildMap {
fun getMangasCategories(mangaIDs: List<Int>): Map<Int, List<CategoryDataClass>> =
buildMap {
transaction {
CategoryMangaTable.innerJoin(CategoryTable)
CategoryMangaTable
.innerJoin(CategoryTable)
.select { CategoryMangaTable.manga inList mangaIDs }
.groupBy { it[CategoryMangaTable.manga] }
.forEach {
@@ -143,5 +149,4 @@ object CategoryManga {
}
}
}
}
}
@@ -50,14 +50,13 @@ import java.util.TreeSet
import java.util.concurrent.TimeUnit
import kotlin.math.max
private fun List<ChapterDataClass>.removeDuplicates(currentChapter: ChapterDataClass): List<ChapterDataClass> {
return groupBy { it.chapterNumber }
private fun List<ChapterDataClass>.removeDuplicates(currentChapter: ChapterDataClass): List<ChapterDataClass> =
groupBy { it.chapterNumber }
.map { (_, chapters) ->
chapters.find { it.id == currentChapter.id }
?: chapters.find { it.scanlator == currentChapter.scanlator }
?: chapters.first()
}
}
object Chapter {
private val logger = KotlinLogging.logger { }
@@ -66,12 +65,13 @@ object Chapter {
suspend fun getChapterList(
mangaId: Int,
onlineFetch: Boolean = false,
): List<ChapterDataClass> {
return if (onlineFetch) {
): List<ChapterDataClass> =
if (onlineFetch) {
getSourceChapters(mangaId)
} else {
transaction {
ChapterTable.select { ChapterTable.manga eq mangaId }
ChapterTable
.select { ChapterTable.manga eq mangaId }
.orderBy(ChapterTable.sourceOrder to SortOrder.DESC)
.map {
ChapterTable.toDataClass(it)
@@ -80,18 +80,16 @@ object Chapter {
getSourceChapters(mangaId)
}
}
}
fun getCountOfMangaChapters(mangaId: Int): Int {
return transaction { ChapterTable.select { ChapterTable.manga eq mangaId }.count().toInt() }
}
fun getCountOfMangaChapters(mangaId: Int): Int = transaction { ChapterTable.select { ChapterTable.manga eq mangaId }.count().toInt() }
private suspend fun getSourceChapters(mangaId: Int): List<ChapterDataClass> {
val chapterList = fetchChapterList(mangaId)
val dbChapterMap =
transaction {
ChapterTable.select { ChapterTable.manga eq mangaId }
ChapterTable
.select { ChapterTable.manga eq mangaId }
.associateBy({ it[ChapterTable.url] }, { it })
}
@@ -126,7 +124,8 @@ object Chapter {
}
val map: Cache<Int, Mutex> =
CacheBuilder.newBuilder()
CacheBuilder
.newBuilder()
.expireAfterAccess(10, TimeUnit.MINUTES)
.build()
@@ -173,7 +172,8 @@ object Chapter {
val chaptersInDb =
transaction {
ChapterTable.select { ChapterTable.manga eq mangaId }
ChapterTable
.select { ChapterTable.manga eq mangaId }
.map { ChapterTable.toDataClass(it) }
.toList()
}
@@ -259,39 +259,40 @@ object Chapter {
transaction {
if (chaptersToInsert.isNotEmpty()) {
ChapterTable.batchInsert(chaptersToInsert) { chapter ->
this[ChapterTable.url] = chapter.url
this[ChapterTable.name] = chapter.name
this[ChapterTable.date_upload] = chapter.uploadDate
this[ChapterTable.chapter_number] = chapter.chapterNumber
this[ChapterTable.scanlator] = chapter.scanlator
this[ChapterTable.sourceOrder] = chapter.index
this[ChapterTable.fetchedAt] = chapter.fetchedAt
this[ChapterTable.manga] = chapter.mangaId
this[ChapterTable.realUrl] = chapter.realUrl
this[ChapterTable.isRead] = false
this[ChapterTable.isBookmarked] = false
this[ChapterTable.isDownloaded] = false
ChapterTable
.batchInsert(chaptersToInsert) { chapter ->
this[ChapterTable.url] = chapter.url
this[ChapterTable.name] = chapter.name
this[ChapterTable.date_upload] = chapter.uploadDate
this[ChapterTable.chapter_number] = chapter.chapterNumber
this[ChapterTable.scanlator] = chapter.scanlator
this[ChapterTable.sourceOrder] = chapter.index
this[ChapterTable.fetchedAt] = chapter.fetchedAt
this[ChapterTable.manga] = chapter.mangaId
this[ChapterTable.realUrl] = chapter.realUrl
this[ChapterTable.isRead] = false
this[ChapterTable.isBookmarked] = false
this[ChapterTable.isDownloaded] = false
// is recognized chapter number
if (chapter.chapterNumber >= 0f && chapter.chapterNumber in deletedChapterNumbers) {
this[ChapterTable.isRead] = chapter.chapterNumber in deletedReadChapterNumbers
this[ChapterTable.isBookmarked] = chapter.chapterNumber in deletedBookmarkedChapterNumbers
// is recognized chapter number
if (chapter.chapterNumber >= 0f && chapter.chapterNumber in deletedChapterNumbers) {
this[ChapterTable.isRead] = chapter.chapterNumber in deletedReadChapterNumbers
this[ChapterTable.isBookmarked] = chapter.chapterNumber in deletedBookmarkedChapterNumbers
// only preserve download status for chapters of the same scanlator, otherwise,
// the downloaded files won't be found anyway
val downloadedChapterInfo = deletedDownloadedChapterNumberInfoMap[chapter.chapterNumber]
val pageCount = downloadedChapterInfo?.get(chapter.scanlator)
if (pageCount != null) {
this[ChapterTable.isDownloaded] = true
this[ChapterTable.pageCount] = pageCount
// only preserve download status for chapters of the same scanlator, otherwise,
// the downloaded files won't be found anyway
val downloadedChapterInfo = deletedDownloadedChapterNumberInfoMap[chapter.chapterNumber]
val pageCount = downloadedChapterInfo?.get(chapter.scanlator)
if (pageCount != null) {
this[ChapterTable.isDownloaded] = true
this[ChapterTable.pageCount] = pageCount
}
// Try to use the fetch date of the original entry to not pollute 'Updates' tab
deletedChapterNumberDateFetchMap[chapter.chapterNumber]?.let {
this[ChapterTable.fetchedAt] = it
}
}
// Try to use the fetch date of the original entry to not pollute 'Updates' tab
deletedChapterNumberDateFetchMap[chapter.chapterNumber]?.let {
this[ChapterTable.fetchedAt] = it
}
}
}.forEach { insertedChapters.add(ChapterTable.toDataClass(it)) }
}.forEach { insertedChapters.add(ChapterTable.toDataClass(it)) }
}
if (chaptersToUpdate.isNotEmpty()) {
@@ -531,7 +532,8 @@ object Chapter {
if (isRead == true) {
val mangaIds =
transaction {
ChapterTable.select { condition }
ChapterTable
.select { condition }
.map { it[ChapterTable.manga].value }
.toSet()
}
@@ -539,21 +541,21 @@ object Chapter {
}
}
fun getChaptersMetaMaps(chapterIds: List<EntityID<Int>>): Map<EntityID<Int>, Map<String, String>> {
return transaction {
ChapterMetaTable.select { ChapterMetaTable.ref inList chapterIds }
fun getChaptersMetaMaps(chapterIds: List<EntityID<Int>>): Map<EntityID<Int>, Map<String, String>> =
transaction {
ChapterMetaTable
.select { ChapterMetaTable.ref inList chapterIds }
.groupBy { it[ChapterMetaTable.ref] }
.mapValues { it.value.associate { it[ChapterMetaTable.key] to it[ChapterMetaTable.value] } }
.withDefault { emptyMap<String, String>() }
}
}
fun getChapterMetaMap(chapter: EntityID<Int>): Map<String, String> {
return transaction {
ChapterMetaTable.select { ChapterMetaTable.ref eq chapter }
fun getChapterMetaMap(chapter: EntityID<Int>): Map<String, String> =
transaction {
ChapterMetaTable
.select { ChapterMetaTable.ref eq chapter }
.associate { it[ChapterMetaTable.key] to it[ChapterMetaTable.value] }
}
}
fun modifyChapterMeta(
mangaId: Int,
@@ -563,8 +565,10 @@ object Chapter {
) {
transaction {
val chapterId =
ChapterTable.select { (ChapterTable.manga eq mangaId) and (ChapterTable.sourceOrder eq chapterIndex) }
.first()[ChapterTable.id].value
ChapterTable
.select { (ChapterTable.manga eq mangaId) and (ChapterTable.sourceOrder eq chapterIndex) }
.first()[ChapterTable.id]
.value
modifyChapterMeta(chapterId, key, value)
}
}
@@ -576,7 +580,8 @@ object Chapter {
) {
transaction {
val meta =
ChapterMetaTable.select { (ChapterMetaTable.ref eq chapterId) and (ChapterMetaTable.key eq key) }
ChapterMetaTable
.select { (ChapterMetaTable.ref eq chapterId) and (ChapterMetaTable.key eq key) }
.firstOrNull()
if (meta == null) {
@@ -599,8 +604,10 @@ object Chapter {
) {
transaction {
val chapterId =
ChapterTable.select { (ChapterTable.manga eq mangaId) and (ChapterTable.sourceOrder eq chapterIndex) }
.first()[ChapterTable.id].value
ChapterTable
.select { (ChapterTable.manga eq mangaId) and (ChapterTable.sourceOrder eq chapterIndex) }
.first()[ChapterTable.id]
.value
ChapterDownloadHelper.delete(mangaId, chapterId)
@@ -619,7 +626,8 @@ object Chapter {
} else if (input.chapterIndexes != null && mangaId != null) {
transaction {
val chapterIds =
ChapterTable.slice(ChapterTable.manga, ChapterTable.id)
ChapterTable
.slice(ChapterTable.manga, ChapterTable.id)
.select { (ChapterTable.sourceOrder inList input.chapterIndexes) and (ChapterTable.manga eq mangaId) }
.map { row ->
val chapterId = row[ChapterTable.id].value
@@ -637,7 +645,8 @@ object Chapter {
fun deleteChapters(chapterIds: List<Int>) {
transaction {
ChapterTable.slice(ChapterTable.manga, ChapterTable.id)
ChapterTable
.slice(ChapterTable.manga, ChapterTable.id)
.select { ChapterTable.id inList chapterIds }
.forEach { row ->
val chapterMangaId = row[ChapterTable.manga].value
@@ -651,8 +660,8 @@ object Chapter {
}
}
fun getRecentChapters(pageNum: Int): PaginatedList<MangaChapterDataClass> {
return paginatedFrom(pageNum) {
fun getRecentChapters(pageNum: Int): PaginatedList<MangaChapterDataClass> =
paginatedFrom(pageNum) {
transaction {
(ChapterTable innerJoin MangaTable)
.select { (MangaTable.inLibrary eq true) and (ChapterTable.fetchedAt greater MangaTable.inLibraryAt) }
@@ -665,5 +674,4 @@ object Chapter {
}
}
}
}
}
@@ -16,16 +16,12 @@ object ChapterDownloadHelper {
mangaId: Int,
chapterId: Int,
index: Int,
): Pair<InputStream, String> {
return provider(mangaId, chapterId).getImage().execute(index)
}
): Pair<InputStream, String> = provider(mangaId, chapterId).getImage().execute(index)
fun delete(
mangaId: Int,
chapterId: Int,
): Boolean {
return provider(mangaId, chapterId).delete()
}
): Boolean = provider(mangaId, chapterId).delete()
suspend fun download(
mangaId: Int,
@@ -33,9 +29,7 @@ object ChapterDownloadHelper {
download: DownloadChapter,
scope: CoroutineScope,
step: suspend (DownloadChapter?, Boolean) -> Unit,
): Boolean {
return provider(mangaId, chapterId).download().execute(download, scope, step)
}
): Boolean = provider(mangaId, chapterId).download().execute(download, scope, step)
// return the appropriate provider based on how the download was saved. For the logic is simple but will evolve when new types of downloads are available
private fun provider(

Some files were not shown because too many files have changed in this diff Show More