mirror of
https://github.com/Suwayomi/Suwayomi-Server.git
synced 2026-07-11 23:04:33 -05:00
[skip ci] Formatting
This commit is contained in:
@@ -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(
|
||||
|
||||
@@ -30,9 +30,10 @@ object Library {
|
||||
if (!manga.inLibrary) {
|
||||
transaction {
|
||||
val defaultCategories =
|
||||
CategoryTable.select {
|
||||
(CategoryTable.isDefault eq true) and (CategoryTable.id neq Category.DEFAULT_CATEGORY_ID)
|
||||
}.toList()
|
||||
CategoryTable
|
||||
.select {
|
||||
(CategoryTable.isDefault eq true) and (CategoryTable.id neq Category.DEFAULT_CATEGORY_ID)
|
||||
}.toList()
|
||||
val existingCategories = CategoryMangaTable.select { CategoryMangaTable.manga eq mangaId }.toList()
|
||||
|
||||
MangaTable.update({ MangaTable.id eq manga.id }) {
|
||||
|
||||
@@ -64,13 +64,12 @@ object Manga {
|
||||
private fun truncate(
|
||||
text: String?,
|
||||
maxLength: Int,
|
||||
): String? {
|
||||
return if (text?.length ?: 0 > maxLength) {
|
||||
): String? =
|
||||
if (text?.length ?: 0 > maxLength) {
|
||||
text?.take(maxLength - 3) + "..."
|
||||
} else {
|
||||
text
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getManga(
|
||||
mangaId: Int,
|
||||
@@ -227,12 +226,12 @@ object Manga {
|
||||
trackers = Track.getTrackRecordsByMangaId(mangaId),
|
||||
)
|
||||
|
||||
fun getMangaMetaMap(mangaId: Int): Map<String, String> {
|
||||
return transaction {
|
||||
MangaMetaTable.select { MangaMetaTable.ref eq mangaId }
|
||||
fun getMangaMetaMap(mangaId: Int): Map<String, String> =
|
||||
transaction {
|
||||
MangaMetaTable
|
||||
.select { MangaMetaTable.ref eq mangaId }
|
||||
.associate { it[MangaMetaTable.key] to it[MangaMetaTable.value] }
|
||||
}
|
||||
}
|
||||
|
||||
fun modifyMangaMeta(
|
||||
mangaId: Int,
|
||||
@@ -241,7 +240,8 @@ object 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()
|
||||
|
||||
if (meta == null) {
|
||||
@@ -286,9 +286,10 @@ object Manga {
|
||||
} ?: throw NullPointerException("No thumbnail found")
|
||||
|
||||
return try {
|
||||
source.client.newCall(
|
||||
GET(thumbnailUrl, source.headers, cache = CacheControl.FORCE_NETWORK),
|
||||
).awaitSuccess()
|
||||
source.client
|
||||
.newCall(
|
||||
GET(thumbnailUrl, source.headers, cache = CacheControl.FORCE_NETWORK),
|
||||
).awaitSuccess()
|
||||
} catch (e: HttpException) {
|
||||
val tryToRefreshUrl =
|
||||
!refreshUrl &&
|
||||
@@ -341,9 +342,10 @@ object Manga {
|
||||
val thumbnailUrl =
|
||||
mangaEntry[MangaTable.thumbnail_url]
|
||||
?: throw NullPointerException("No thumbnail found")
|
||||
network.client.newCall(
|
||||
GET(thumbnailUrl, cache = CacheControl.FORCE_NETWORK),
|
||||
).await()
|
||||
network.client
|
||||
.newCall(
|
||||
GET(thumbnailUrl, cache = CacheControl.FORCE_NETWORK),
|
||||
).await()
|
||||
}
|
||||
|
||||
else -> throw IllegalArgumentException("Unknown source")
|
||||
@@ -372,19 +374,18 @@ object Manga {
|
||||
clearCachedImage(applicationDirs.thumbnailDownloadsRoot, fileName)
|
||||
}
|
||||
|
||||
fun getLatestChapter(mangaId: Int): ChapterDataClass? {
|
||||
return transaction {
|
||||
fun getLatestChapter(mangaId: Int): ChapterDataClass? =
|
||||
transaction {
|
||||
ChapterTable.select { ChapterTable.manga eq mangaId }.maxByOrNull { it[ChapterTable.sourceOrder] }
|
||||
}?.let { ChapterTable.toDataClass(it) }
|
||||
}
|
||||
|
||||
fun getUnreadChapters(mangaId: Int): List<ChapterDataClass> {
|
||||
return transaction {
|
||||
ChapterTable.select { (ChapterTable.manga eq mangaId) and (ChapterTable.isRead eq false) }
|
||||
fun getUnreadChapters(mangaId: Int): List<ChapterDataClass> =
|
||||
transaction {
|
||||
ChapterTable
|
||||
.select { (ChapterTable.manga eq mangaId) and (ChapterTable.isRead eq false) }
|
||||
.orderBy(ChapterTable.sourceOrder to SortOrder.DESC)
|
||||
.map { ChapterTable.toDataClass(it) }
|
||||
}
|
||||
}
|
||||
|
||||
fun isInIncludedDownloadCategory(
|
||||
logContext: KLogger = logger,
|
||||
|
||||
@@ -21,9 +21,7 @@ import suwayomi.tachidesk.manga.model.table.toDataClass
|
||||
import java.time.Instant
|
||||
|
||||
object MangaList {
|
||||
fun proxyThumbnailUrl(mangaId: Int): String {
|
||||
return "/api/v1/manga/$mangaId/thumbnail"
|
||||
}
|
||||
fun proxyThumbnailUrl(mangaId: Int): String = "/api/v1/manga/$mangaId/thumbnail"
|
||||
|
||||
suspend fun getMangaList(
|
||||
sourceId: Long,
|
||||
@@ -47,41 +45,44 @@ object MangaList {
|
||||
return mangasPage.processEntries(sourceId)
|
||||
}
|
||||
|
||||
fun MangasPage.insertOrUpdate(sourceId: Long): List<Int> {
|
||||
return transaction {
|
||||
fun MangasPage.insertOrUpdate(sourceId: Long): List<Int> =
|
||||
transaction {
|
||||
val existingMangaUrlsToId =
|
||||
MangaTable.select {
|
||||
(MangaTable.sourceReference eq sourceId) and (MangaTable.url inList mangas.map { it.url })
|
||||
}.associateBy { it[MangaTable.url] }
|
||||
MangaTable
|
||||
.select {
|
||||
(MangaTable.sourceReference eq sourceId) and (MangaTable.url inList mangas.map { it.url })
|
||||
}.associateBy { it[MangaTable.url] }
|
||||
val existingMangaUrls = existingMangaUrlsToId.map { it.key }
|
||||
|
||||
val mangasToInsert = mangas.filter { !existingMangaUrls.contains(it.url) }
|
||||
|
||||
val insertedMangaUrlsToId =
|
||||
MangaTable.batchInsert(mangasToInsert) {
|
||||
this[MangaTable.url] = it.url
|
||||
this[MangaTable.title] = it.title
|
||||
MangaTable
|
||||
.batchInsert(mangasToInsert) {
|
||||
this[MangaTable.url] = it.url
|
||||
this[MangaTable.title] = it.title
|
||||
|
||||
this[MangaTable.artist] = it.artist
|
||||
this[MangaTable.author] = it.author
|
||||
this[MangaTable.description] = it.description
|
||||
this[MangaTable.genre] = it.genre
|
||||
this[MangaTable.status] = it.status
|
||||
this[MangaTable.thumbnail_url] = it.thumbnail_url
|
||||
this[MangaTable.updateStrategy] = it.update_strategy.name
|
||||
this[MangaTable.artist] = it.artist
|
||||
this[MangaTable.author] = it.author
|
||||
this[MangaTable.description] = it.description
|
||||
this[MangaTable.genre] = it.genre
|
||||
this[MangaTable.status] = it.status
|
||||
this[MangaTable.thumbnail_url] = it.thumbnail_url
|
||||
this[MangaTable.updateStrategy] = it.update_strategy.name
|
||||
|
||||
this[MangaTable.sourceReference] = sourceId
|
||||
}.associate { Pair(it[MangaTable.url], it[MangaTable.id].value) }
|
||||
this[MangaTable.sourceReference] = sourceId
|
||||
}.associate { Pair(it[MangaTable.url], it[MangaTable.id].value) }
|
||||
|
||||
// delete thumbnail in case cached data still exists
|
||||
insertedMangaUrlsToId.forEach { (_, id) -> Manga.clearThumbnail(id) }
|
||||
|
||||
val mangaToUpdate =
|
||||
mangas.mapNotNull { sManga ->
|
||||
existingMangaUrlsToId[sManga.url]?.let { sManga to it }
|
||||
}.filterNot { (_, resultRow) ->
|
||||
resultRow[MangaTable.inLibrary]
|
||||
}
|
||||
mangas
|
||||
.mapNotNull { sManga ->
|
||||
existingMangaUrlsToId[sManga.url]?.let { sManga to it }
|
||||
}.filterNot { (_, resultRow) ->
|
||||
resultRow[MangaTable.inLibrary]
|
||||
}
|
||||
|
||||
if (mangaToUpdate.isNotEmpty()) {
|
||||
BatchUpdateStatement(MangaTable).apply {
|
||||
@@ -116,7 +117,6 @@ object MangaList {
|
||||
?: throw Exception("MangaList::insertOrGet($sourceId): Something went wrong inserting browsed source mangas")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun MangasPage.processEntries(sourceId: Long): PagedMangaListDataClass {
|
||||
val mangasPage = this
|
||||
|
||||
@@ -51,17 +51,20 @@ object Page {
|
||||
val source = getCatalogueSourceOrStub(mangaEntry[MangaTable.sourceReference])
|
||||
val chapterEntry =
|
||||
transaction {
|
||||
ChapterTable.select {
|
||||
(ChapterTable.sourceOrder eq chapterIndex) and (ChapterTable.manga eq mangaId)
|
||||
}.first()
|
||||
ChapterTable
|
||||
.select {
|
||||
(ChapterTable.sourceOrder eq chapterIndex) and (ChapterTable.manga eq mangaId)
|
||||
}.first()
|
||||
}
|
||||
val chapterId = chapterEntry[ChapterTable.id].value
|
||||
|
||||
val pageEntry =
|
||||
transaction {
|
||||
PageTable.select { (PageTable.chapter eq chapterId) }
|
||||
PageTable
|
||||
.select { (PageTable.chapter eq chapterId) }
|
||||
.orderBy(PageTable.index to SortOrder.ASC)
|
||||
.limit(1, index.toLong()).first()
|
||||
.limit(1, index.toLong())
|
||||
.first()
|
||||
}
|
||||
val tachiyomiPage =
|
||||
Page(
|
||||
@@ -114,7 +117,5 @@ object Page {
|
||||
}
|
||||
|
||||
/** converts 0 to "001" */
|
||||
fun getPageName(index: Int): String {
|
||||
return String.format("%03d", index + 1)
|
||||
}
|
||||
fun getPageName(index: Int): String = String.format("%03d", index + 1)
|
||||
}
|
||||
|
||||
@@ -95,7 +95,10 @@ object Search {
|
||||
|
||||
private fun Filter.Select<*>.getValuesType(): String = values::class.java.componentType!!.simpleName
|
||||
|
||||
class SerializableGroup(name: String, state: List<FilterObject>) : Filter<List<FilterObject>>(name, state)
|
||||
class SerializableGroup(
|
||||
name: String,
|
||||
state: List<FilterObject>,
|
||||
) : Filter<List<FilterObject>>(name, state)
|
||||
|
||||
data class FilterObject(
|
||||
val type: String,
|
||||
|
||||
@@ -97,11 +97,10 @@ object Source {
|
||||
/**
|
||||
* Gets a source's PreferenceScreen, puts the result into [preferenceScreenMap]
|
||||
*/
|
||||
fun getSourcePreferences(sourceId: Long): List<PreferenceObject> {
|
||||
return getSourcePreferencesRaw(sourceId).map {
|
||||
fun getSourcePreferences(sourceId: Long): List<PreferenceObject> =
|
||||
getSourcePreferencesRaw(sourceId).map {
|
||||
PreferenceObject(it::class.java.simpleName, it)
|
||||
}
|
||||
}
|
||||
|
||||
fun getSourcePreferencesRaw(sourceId: Long): List<Preference> {
|
||||
val source = getCatalogueSourceOrStub(sourceId)
|
||||
|
||||
@@ -4,20 +4,12 @@ import suwayomi.tachidesk.manga.impl.download.fileProvider.impl.ThumbnailFilePro
|
||||
import java.io.InputStream
|
||||
|
||||
object ThumbnailDownloadHelper {
|
||||
fun getImage(mangaId: Int): Pair<InputStream, String> {
|
||||
return provider(mangaId).getImage().execute()
|
||||
}
|
||||
fun getImage(mangaId: Int): Pair<InputStream, String> = provider(mangaId).getImage().execute()
|
||||
|
||||
fun delete(mangaId: Int): Boolean {
|
||||
return provider(mangaId).delete()
|
||||
}
|
||||
fun delete(mangaId: Int): Boolean = provider(mangaId).delete()
|
||||
|
||||
suspend fun download(mangaId: Int): Boolean {
|
||||
return provider(mangaId).download().execute()
|
||||
}
|
||||
suspend fun download(mangaId: Int): Boolean = provider(mangaId).download().execute()
|
||||
|
||||
// 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(mangaId: Int): ThumbnailFileProvider {
|
||||
return ThumbnailFileProvider(mangaId)
|
||||
}
|
||||
private fun provider(mangaId: Int): ThumbnailFileProvider = ThumbnailFileProvider(mangaId)
|
||||
}
|
||||
|
||||
@@ -5,7 +5,9 @@ package suwayomi.tachidesk.manga.impl.backup.models
|
||||
import eu.kanade.tachiyomi.source.model.SChapter
|
||||
import java.io.Serializable
|
||||
|
||||
interface Chapter : SChapter, Serializable {
|
||||
interface Chapter :
|
||||
SChapter,
|
||||
Serializable {
|
||||
var id: Long?
|
||||
|
||||
var manga_id: Long?
|
||||
|
||||
@@ -36,7 +36,5 @@ class ChapterImpl : Chapter {
|
||||
return id == chapter.id
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return url.hashCode() + id.hashCode()
|
||||
}
|
||||
override fun hashCode(): Int = url.hashCode() + id.hashCode()
|
||||
}
|
||||
|
||||
@@ -41,13 +41,9 @@ interface Manga : SManga {
|
||||
setChapterFlags(order, CHAPTER_SORT_MASK)
|
||||
}
|
||||
|
||||
fun sortDescending(): Boolean {
|
||||
return chapter_flags and CHAPTER_SORT_MASK == CHAPTER_SORT_DESC
|
||||
}
|
||||
fun sortDescending(): Boolean = chapter_flags and CHAPTER_SORT_MASK == CHAPTER_SORT_DESC
|
||||
|
||||
fun getGenres(): List<String>? {
|
||||
return genre?.split(", ")?.map { it.trim() }
|
||||
}
|
||||
fun getGenres(): List<String>? = genre?.split(", ")?.map { it.trim() }
|
||||
|
||||
private fun setChapterFlags(
|
||||
flag: Int,
|
||||
|
||||
@@ -63,7 +63,5 @@ open class MangaImpl : Manga {
|
||||
return id == manga.id
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return url.hashCode() + id.hashCode()
|
||||
}
|
||||
override fun hashCode(): Int = url.hashCode() + id.hashCode()
|
||||
}
|
||||
|
||||
@@ -93,10 +93,15 @@ object ProtoBackupExport : ProtoBackupBase() {
|
||||
}
|
||||
}
|
||||
|
||||
val (hour, minute) = serverConfig.backupTime.value.split(":").map { it.toInt() }
|
||||
val (hour, minute) =
|
||||
serverConfig.backupTime.value
|
||||
.split(":")
|
||||
.map { it.toInt() }
|
||||
val backupHour = hour.coerceAtLeast(0).coerceAtMost(23)
|
||||
val backupMinute = minute.coerceAtLeast(0).coerceAtMost(59)
|
||||
val backupInterval = serverConfig.backupInterval.value.days.coerceAtLeast(1.days)
|
||||
val backupInterval =
|
||||
serverConfig.backupInterval.value.days
|
||||
.coerceAtLeast(1.days)
|
||||
|
||||
// trigger last backup in case the server wasn't running on the scheduled time
|
||||
val lastAutomatedBackup = preferences.getLong(LAST_AUTOMATED_BACKUP_KEY, 0)
|
||||
@@ -161,7 +166,10 @@ object ProtoBackupExport : ProtoBackupBase() {
|
||||
|
||||
val lastAccessTime = file.lastModified()
|
||||
val isTTLReached =
|
||||
System.currentTimeMillis() - lastAccessTime >= serverConfig.backupTTL.value.days.coerceAtLeast(1.days).inWholeMilliseconds
|
||||
System.currentTimeMillis() - lastAccessTime >=
|
||||
serverConfig.backupTTL.value.days
|
||||
.coerceAtLeast(1.days)
|
||||
.inWholeMilliseconds
|
||||
if (isTTLReached) {
|
||||
file.delete()
|
||||
}
|
||||
@@ -185,7 +193,11 @@ object ProtoBackupExport : ProtoBackupBase() {
|
||||
val byteArray = parser.encodeToByteArray(BackupSerializer, backup)
|
||||
|
||||
val byteStream = ByteArrayOutputStream()
|
||||
byteStream.sink().gzip().buffer().use { it.write(byteArray) }
|
||||
byteStream
|
||||
.sink()
|
||||
.gzip()
|
||||
.buffer()
|
||||
.use { it.write(byteArray) }
|
||||
|
||||
return byteStream.toByteArray().inputStream()
|
||||
}
|
||||
@@ -193,8 +205,8 @@ object ProtoBackupExport : ProtoBackupBase() {
|
||||
private fun backupManga(
|
||||
databaseManga: Query,
|
||||
flags: BackupFlags,
|
||||
): List<BackupManga> {
|
||||
return databaseManga.map { mangaRow ->
|
||||
): List<BackupManga> =
|
||||
databaseManga.map { mangaRow ->
|
||||
val backupManga =
|
||||
BackupManga(
|
||||
source = mangaRow[MangaTable.sourceReference],
|
||||
@@ -216,7 +228,8 @@ object ProtoBackupExport : ProtoBackupBase() {
|
||||
if (flags.includeChapters) {
|
||||
val chapters =
|
||||
transaction {
|
||||
ChapterTable.select { ChapterTable.manga eq mangaId }
|
||||
ChapterTable
|
||||
.select { ChapterTable.manga eq mangaId }
|
||||
.orderBy(ChapterTable.sourceOrder to SortOrder.DESC)
|
||||
.map {
|
||||
ChapterTable.toDataClass(it)
|
||||
@@ -277,22 +290,23 @@ object ProtoBackupExport : ProtoBackupBase() {
|
||||
|
||||
backupManga
|
||||
}
|
||||
}
|
||||
|
||||
private fun backupCategories(): List<BackupCategory> {
|
||||
return CategoryTable.selectAll().orderBy(CategoryTable.order to SortOrder.ASC).map {
|
||||
CategoryTable.toDataClass(it)
|
||||
}.map {
|
||||
BackupCategory(
|
||||
it.name,
|
||||
it.order,
|
||||
0, // not supported in Tachidesk
|
||||
)
|
||||
}
|
||||
}
|
||||
private fun backupCategories(): List<BackupCategory> =
|
||||
CategoryTable
|
||||
.selectAll()
|
||||
.orderBy(CategoryTable.order to SortOrder.ASC)
|
||||
.map {
|
||||
CategoryTable.toDataClass(it)
|
||||
}.map {
|
||||
BackupCategory(
|
||||
it.name,
|
||||
it.order,
|
||||
0, // not supported in Tachidesk
|
||||
)
|
||||
}
|
||||
|
||||
private fun backupExtensionInfo(mangas: Query): List<BackupSource> {
|
||||
return mangas
|
||||
private fun backupExtensionInfo(mangas: Query): List<BackupSource> =
|
||||
mangas
|
||||
.asSequence()
|
||||
.map { it[MangaTable.sourceReference] }
|
||||
.distinct()
|
||||
@@ -302,7 +316,5 @@ object ProtoBackupExport : ProtoBackupBase() {
|
||||
sourceRow?.get(SourceTable.name) ?: "",
|
||||
it,
|
||||
)
|
||||
}
|
||||
.toList()
|
||||
}
|
||||
}.toList()
|
||||
}
|
||||
|
||||
@@ -74,18 +74,22 @@ object ProtoBackupImport : ProtoBackupBase() {
|
||||
|
||||
data object Failure : BackupRestoreState()
|
||||
|
||||
data class RestoringCategories(val totalManga: Int) : BackupRestoreState()
|
||||
data class RestoringCategories(
|
||||
val totalManga: Int,
|
||||
) : BackupRestoreState()
|
||||
|
||||
data class RestoringManga(val current: Int, val totalManga: Int, val title: String) : BackupRestoreState()
|
||||
data class RestoringManga(
|
||||
val current: Int,
|
||||
val totalManga: Int,
|
||||
val title: String,
|
||||
) : BackupRestoreState()
|
||||
}
|
||||
|
||||
private val backupRestoreIdToState = mutableMapOf<String, BackupRestoreState>()
|
||||
|
||||
val notifyFlow = MutableSharedFlow<Unit>(extraBufferCapacity = 1, onBufferOverflow = DROP_OLDEST)
|
||||
|
||||
fun getRestoreState(id: String): BackupRestoreState? {
|
||||
return backupRestoreIdToState[id]
|
||||
}
|
||||
fun getRestoreState(id: String): BackupRestoreState? = backupRestoreIdToState[id]
|
||||
|
||||
private fun updateRestoreState(
|
||||
id: String,
|
||||
@@ -131,8 +135,8 @@ object ProtoBackupImport : ProtoBackupBase() {
|
||||
suspend fun restoreLegacy(
|
||||
sourceStream: InputStream,
|
||||
restoreId: String = "legacy",
|
||||
): ValidationResult {
|
||||
return backupMutex.withLock {
|
||||
): ValidationResult =
|
||||
backupMutex.withLock {
|
||||
try {
|
||||
logger.info { "restore($restoreId): restoring..." }
|
||||
performRestore(restoreId, sourceStream)
|
||||
@@ -151,13 +155,17 @@ object ProtoBackupImport : ProtoBackupBase() {
|
||||
cleanupRestoreState(restoreId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun performRestore(
|
||||
id: String,
|
||||
sourceStream: InputStream,
|
||||
): ValidationResult {
|
||||
val backupString = sourceStream.source().gzip().buffer().use { it.readByteArray() }
|
||||
val backupString =
|
||||
sourceStream
|
||||
.source()
|
||||
.gzip()
|
||||
.buffer()
|
||||
.use { it.readByteArray() }
|
||||
val backup = parser.decodeFromByteArray(BackupSerializer, backupString)
|
||||
|
||||
val validationResult = validate(backup)
|
||||
@@ -174,7 +182,8 @@ object ProtoBackupImport : ProtoBackupBase() {
|
||||
transaction {
|
||||
backup.backupCategories.associate {
|
||||
val dbCategory =
|
||||
CategoryTable.select { CategoryTable.name eq it.name }
|
||||
CategoryTable
|
||||
.select { CategoryTable.name eq it.name }
|
||||
.firstOrNull()
|
||||
val categoryId =
|
||||
dbCategory?.let { categoryResultRow ->
|
||||
@@ -265,7 +274,8 @@ object ProtoBackupImport : ProtoBackupBase() {
|
||||
) {
|
||||
val dbManga =
|
||||
transaction {
|
||||
MangaTable.select { (MangaTable.url eq manga.url) and (MangaTable.sourceReference eq manga.source) }
|
||||
MangaTable
|
||||
.select { (MangaTable.url eq manga.url) and (MangaTable.sourceReference eq manga.source) }
|
||||
.firstOrNull()
|
||||
}
|
||||
|
||||
@@ -274,26 +284,27 @@ object ProtoBackupImport : ProtoBackupBase() {
|
||||
transaction {
|
||||
// insert manga to database
|
||||
val mangaId =
|
||||
MangaTable.insertAndGetId {
|
||||
it[url] = manga.url
|
||||
it[title] = manga.title
|
||||
MangaTable
|
||||
.insertAndGetId {
|
||||
it[url] = manga.url
|
||||
it[title] = manga.title
|
||||
|
||||
it[artist] = manga.artist
|
||||
it[author] = manga.author
|
||||
it[description] = manga.description
|
||||
it[genre] = manga.genre
|
||||
it[status] = manga.status
|
||||
it[thumbnail_url] = manga.thumbnail_url
|
||||
it[updateStrategy] = manga.update_strategy.name
|
||||
it[artist] = manga.artist
|
||||
it[author] = manga.author
|
||||
it[description] = manga.description
|
||||
it[genre] = manga.genre
|
||||
it[status] = manga.status
|
||||
it[thumbnail_url] = manga.thumbnail_url
|
||||
it[updateStrategy] = manga.update_strategy.name
|
||||
|
||||
it[sourceReference] = manga.source
|
||||
it[sourceReference] = manga.source
|
||||
|
||||
it[initialized] = manga.description != null
|
||||
it[initialized] = manga.description != null
|
||||
|
||||
it[inLibrary] = manga.favorite
|
||||
it[inLibrary] = manga.favorite
|
||||
|
||||
it[inLibraryAt] = TimeUnit.MILLISECONDS.toSeconds(manga.date_added)
|
||||
}.value
|
||||
it[inLibraryAt] = TimeUnit.MILLISECONDS.toSeconds(manga.date_added)
|
||||
}.value
|
||||
|
||||
// delete thumbnail in case cached data still exists
|
||||
clearThumbnail(mangaId)
|
||||
@@ -394,34 +405,36 @@ object ProtoBackupImport : ProtoBackupBase() {
|
||||
}
|
||||
|
||||
val dbTrackRecordsByTrackerId =
|
||||
Tracker.getTrackRecordsByMangaId(mangaId)
|
||||
Tracker
|
||||
.getTrackRecordsByMangaId(mangaId)
|
||||
.mapNotNull { it.record?.toTrack() }
|
||||
.associateBy { it.sync_id }
|
||||
|
||||
val (existingTracks, newTracks) =
|
||||
tracks.mapNotNull { backupTrack ->
|
||||
val track = backupTrack.toTrack(mangaId)
|
||||
tracks
|
||||
.mapNotNull { backupTrack ->
|
||||
val track = backupTrack.toTrack(mangaId)
|
||||
|
||||
val isUnsupportedTracker = TrackerManager.getTracker(track.sync_id) == null
|
||||
if (isUnsupportedTracker) {
|
||||
return@mapNotNull null
|
||||
}
|
||||
val isUnsupportedTracker = TrackerManager.getTracker(track.sync_id) == null
|
||||
if (isUnsupportedTracker) {
|
||||
return@mapNotNull null
|
||||
}
|
||||
|
||||
val dbTrack =
|
||||
dbTrackRecordsByTrackerId[backupTrack.syncId]
|
||||
?: // new track
|
||||
return@mapNotNull track
|
||||
val dbTrack =
|
||||
dbTrackRecordsByTrackerId[backupTrack.syncId]
|
||||
?: // new track
|
||||
return@mapNotNull track
|
||||
|
||||
if (track.toTrackRecordDataClass().forComparison() == dbTrack.toTrackRecordDataClass().forComparison()) {
|
||||
return@mapNotNull null
|
||||
}
|
||||
if (track.toTrackRecordDataClass().forComparison() == dbTrack.toTrackRecordDataClass().forComparison()) {
|
||||
return@mapNotNull null
|
||||
}
|
||||
|
||||
dbTrack.also {
|
||||
it.media_id = track.media_id
|
||||
it.library_id = track.library_id
|
||||
it.last_chapter_read = max(dbTrack.last_chapter_read, track.last_chapter_read)
|
||||
}
|
||||
}.partition { (it.id ?: -1) > 0 }
|
||||
dbTrack.also {
|
||||
it.media_id = track.media_id
|
||||
it.library_id = track.library_id
|
||||
it.last_chapter_read = max(dbTrack.last_chapter_read, track.last_chapter_read)
|
||||
}
|
||||
}.partition { (it.id ?: -1) > 0 }
|
||||
|
||||
existingTracks.forEach(Tracker::updateTrackRecord)
|
||||
newTracks.forEach(Tracker::insertTrackRecord)
|
||||
|
||||
@@ -64,7 +64,12 @@ object ProtoBackupValidator {
|
||||
}
|
||||
|
||||
fun validate(sourceStream: InputStream): ValidationResult {
|
||||
val backupString = sourceStream.source().gzip().buffer().use { it.readByteArray() }
|
||||
val backupString =
|
||||
sourceStream
|
||||
.source()
|
||||
.gzip()
|
||||
.buffer()
|
||||
.use { it.readByteArray() }
|
||||
val backup = ProtoBackupImport.parser.decodeFromByteArray(BackupSerializer, backupString)
|
||||
|
||||
return validate(backup)
|
||||
|
||||
@@ -14,10 +14,9 @@ data class Backup(
|
||||
@ProtoNumber(100) var brokenBackupSources: List<BrokenBackupSource> = emptyList(),
|
||||
@ProtoNumber(101) var backupSources: List<BackupSource> = emptyList(),
|
||||
) {
|
||||
fun getSourceMap(): Map<Long, String> {
|
||||
return (brokenBackupSources.map { BackupSource(it.name, it.sourceId) } + backupSources)
|
||||
fun getSourceMap(): Map<Long, String> =
|
||||
(brokenBackupSources.map { BackupSource(it.name, it.sourceId) } + backupSources)
|
||||
.associate { it.sourceId to it.name }
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun getBasename(name: String = ""): String {
|
||||
|
||||
@@ -21,8 +21,8 @@ data class BackupChapter(
|
||||
@ProtoNumber(9) var chapterNumber: Float = 0F,
|
||||
@ProtoNumber(10) var sourceOrder: Int = 0,
|
||||
) {
|
||||
fun toChapterImpl(): ChapterImpl {
|
||||
return ChapterImpl().apply {
|
||||
fun toChapterImpl(): ChapterImpl =
|
||||
ChapterImpl().apply {
|
||||
url = this@BackupChapter.url
|
||||
name = this@BackupChapter.name
|
||||
chapter_number = this@BackupChapter.chapterNumber
|
||||
@@ -34,5 +34,4 @@ data class BackupChapter(
|
||||
date_upload = this@BackupChapter.dateUpload
|
||||
source_order = this@BackupChapter.sourceOrder
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,8 +39,8 @@ data class BackupManga(
|
||||
@ProtoNumber(104) var history: List<BackupHistory> = emptyList(),
|
||||
@ProtoNumber(105) var updateStrategy: UpdateStrategy = UpdateStrategy.ALWAYS_UPDATE,
|
||||
) {
|
||||
fun getMangaImpl(): MangaImpl {
|
||||
return MangaImpl().apply {
|
||||
fun getMangaImpl(): MangaImpl =
|
||||
MangaImpl().apply {
|
||||
url = this@BackupManga.url
|
||||
title = this@BackupManga.title
|
||||
artist = this@BackupManga.artist
|
||||
@@ -56,23 +56,20 @@ data class BackupManga(
|
||||
chapter_flags = this@BackupManga.chapterFlags
|
||||
update_strategy = this@BackupManga.updateStrategy
|
||||
}
|
||||
}
|
||||
|
||||
fun getChaptersImpl(): List<ChapterImpl> {
|
||||
return chapters.map {
|
||||
fun getChaptersImpl(): List<ChapterImpl> =
|
||||
chapters.map {
|
||||
it.toChapterImpl()
|
||||
}
|
||||
}
|
||||
|
||||
fun getTrackingImpl(): List<TrackImpl> {
|
||||
return tracking.map {
|
||||
fun getTrackingImpl(): List<TrackImpl> =
|
||||
tracking.map {
|
||||
it.getTrackingImpl()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun copyFrom(manga: Manga): BackupManga {
|
||||
return BackupManga(
|
||||
fun copyFrom(manga: Manga): BackupManga =
|
||||
BackupManga(
|
||||
url = manga.url,
|
||||
title = manga.title,
|
||||
artist = manga.artist,
|
||||
@@ -88,6 +85,5 @@ data class BackupManga(
|
||||
viewer_flags = manga.viewer_flags,
|
||||
chapterFlags = manga.chapter_flags,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,11 +16,10 @@ data class BackupSource(
|
||||
@ProtoNumber(2) var sourceId: Long,
|
||||
) {
|
||||
companion object {
|
||||
fun copyFrom(source: Source): BackupSource {
|
||||
return BackupSource(
|
||||
fun copyFrom(source: Source): BackupSource =
|
||||
BackupSource(
|
||||
name = source.name,
|
||||
sourceId = source.id,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,8 +28,8 @@ data class BackupTracking(
|
||||
@ProtoNumber(11) var finishedReadingDate: Long = 0,
|
||||
@ProtoNumber(100) var mediaId: Long = 0,
|
||||
) {
|
||||
fun getTrackingImpl(): TrackImpl {
|
||||
return TrackImpl().apply {
|
||||
fun getTrackingImpl(): TrackImpl =
|
||||
TrackImpl().apply {
|
||||
sync_id = this@BackupTracking.syncId
|
||||
media_id =
|
||||
if (this@BackupTracking.mediaIdInt != 0) {
|
||||
@@ -48,11 +48,10 @@ data class BackupTracking(
|
||||
finished_reading_date = this@BackupTracking.finishedReadingDate
|
||||
tracking_url = this@BackupTracking.trackingUrl
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun copyFrom(track: Track): BackupTracking {
|
||||
return BackupTracking(
|
||||
fun copyFrom(track: Track): BackupTracking =
|
||||
BackupTracking(
|
||||
syncId = track.sync_id,
|
||||
mediaId = track.media_id,
|
||||
// forced not null so its compatible with 1.x backup system
|
||||
@@ -67,6 +66,5 @@ data class BackupTracking(
|
||||
finishedReadingDate = track.finished_reading_date,
|
||||
trackingUrl = track.tracking_url,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,16 +37,12 @@ suspend fun getChapterDownloadReady(
|
||||
return chapter.asDownloadReady()
|
||||
}
|
||||
|
||||
suspend fun getChapterDownloadReadyById(chapterId: Int): ChapterDataClass {
|
||||
return getChapterDownloadReady(chapterId = chapterId)
|
||||
}
|
||||
suspend fun getChapterDownloadReadyById(chapterId: Int): ChapterDataClass = getChapterDownloadReady(chapterId = chapterId)
|
||||
|
||||
suspend fun getChapterDownloadReadyByIndex(
|
||||
chapterIndex: Int,
|
||||
mangaId: Int,
|
||||
): ChapterDataClass {
|
||||
return getChapterDownloadReady(chapterIndex = chapterIndex, mangaId = mangaId)
|
||||
}
|
||||
): ChapterDataClass = getChapterDownloadReady(chapterIndex = chapterIndex, mangaId = mangaId)
|
||||
|
||||
private class ChapterForDownload(
|
||||
optChapterId: Int? = null,
|
||||
@@ -101,15 +97,16 @@ private class ChapterForDownload(
|
||||
optChapterIndex: Int? = null,
|
||||
optMangaId: Int? = null,
|
||||
) = transaction {
|
||||
ChapterTable.select {
|
||||
if (optChapterId != null) {
|
||||
ChapterTable.id eq optChapterId
|
||||
} else if (optChapterIndex != null && optMangaId != null) {
|
||||
(ChapterTable.sourceOrder eq optChapterIndex) and (ChapterTable.manga eq optMangaId)
|
||||
} else {
|
||||
throw Exception("'optChapterId' or 'optChapterIndex' and 'optMangaId' have to be passed")
|
||||
}
|
||||
}.first()
|
||||
ChapterTable
|
||||
.select {
|
||||
if (optChapterId != null) {
|
||||
ChapterTable.id eq optChapterId
|
||||
} else if (optChapterIndex != null && optMangaId != null) {
|
||||
(ChapterTable.sourceOrder eq optChapterIndex) and (ChapterTable.manga eq optMangaId)
|
||||
} else {
|
||||
throw Exception("'optChapterId' or 'optChapterIndex' and 'optMangaId' have to be passed")
|
||||
}
|
||||
}.first()
|
||||
}
|
||||
|
||||
private suspend fun fetchPageList(): List<Page> {
|
||||
@@ -166,12 +163,11 @@ private class ChapterForDownload(
|
||||
}
|
||||
}
|
||||
|
||||
private fun firstPageExists(): Boolean {
|
||||
return try {
|
||||
private fun firstPageExists(): Boolean =
|
||||
try {
|
||||
ChapterDownloadHelper.getImage(mangaId, chapterId, 0).first.close()
|
||||
true
|
||||
} catch (e: Exception) {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,12 +63,17 @@ object DownloadManager {
|
||||
private val sharedPreferences =
|
||||
Injekt.get<Application>().getSharedPreferences(DownloadManager::class.jvmName, Context.MODE_PRIVATE)
|
||||
|
||||
private fun loadDownloadQueue(): List<Int> {
|
||||
return sharedPreferences.getStringSet(DOWNLOAD_QUEUE_KEY, emptySet())?.mapNotNull { it.toInt() }.orEmpty()
|
||||
}
|
||||
private fun loadDownloadQueue(): List<Int> =
|
||||
sharedPreferences
|
||||
.getStringSet(DOWNLOAD_QUEUE_KEY, emptySet())
|
||||
?.mapNotNull {
|
||||
it.toInt()
|
||||
}.orEmpty()
|
||||
|
||||
private fun saveDownloadQueue() {
|
||||
sharedPreferences.edit().putStringSet(DOWNLOAD_QUEUE_KEY, downloadQueue.map { it.chapter.id.toString() }.toSet())
|
||||
sharedPreferences
|
||||
.edit()
|
||||
.putStringSet(DOWNLOAD_QUEUE_KEY, downloadQueue.map { it.chapter.id.toString() }.toSet())
|
||||
.apply()
|
||||
}
|
||||
|
||||
@@ -159,8 +164,8 @@ object DownloadManager {
|
||||
}
|
||||
}
|
||||
|
||||
private fun getStatus(): DownloadStatus {
|
||||
return DownloadStatus(
|
||||
private fun getStatus(): DownloadStatus =
|
||||
DownloadStatus(
|
||||
if (downloadQueue.none { it.state == Downloading }) {
|
||||
Status.Stopped
|
||||
} else {
|
||||
@@ -168,7 +173,6 @@ object DownloadManager {
|
||||
},
|
||||
downloadQueue.toList(),
|
||||
)
|
||||
}
|
||||
|
||||
private val downloaderWatch = MutableSharedFlow<Unit>(extraBufferCapacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
|
||||
|
||||
@@ -201,13 +205,13 @@ object DownloadManager {
|
||||
}
|
||||
|
||||
if (runningDownloaders.size < serverConfig.maxSourcesInParallel.value) {
|
||||
availableDownloads.asSequence()
|
||||
availableDownloads
|
||||
.asSequence()
|
||||
.map { it.manga.sourceId }
|
||||
.distinct()
|
||||
.minus(
|
||||
runningDownloaders.map { it.sourceId }.toSet(),
|
||||
)
|
||||
.take(serverConfig.maxSourcesInParallel.value - runningDownloaders.size)
|
||||
).take(serverConfig.maxSourcesInParallel.value - runningDownloaders.size)
|
||||
.map { getDownloader(it) }
|
||||
.forEach {
|
||||
it.start()
|
||||
@@ -271,7 +275,8 @@ object DownloadManager {
|
||||
|
||||
val mangas =
|
||||
transaction {
|
||||
chapters.distinctBy { chapter -> chapter[MangaTable.id] }
|
||||
chapters
|
||||
.distinctBy { chapter -> chapter[MangaTable.id] }
|
||||
.map { MangaTable.toDataClass(it) }
|
||||
.associateBy { it.id }
|
||||
}
|
||||
@@ -420,11 +425,12 @@ object DownloadManager {
|
||||
logger.debug { "stop" }
|
||||
|
||||
coroutineScope {
|
||||
downloaders.map { (_, downloader) ->
|
||||
async {
|
||||
downloader.stop()
|
||||
}
|
||||
}.awaitAll()
|
||||
downloaders
|
||||
.map { (_, downloader) ->
|
||||
async {
|
||||
downloader.stop()
|
||||
}
|
||||
}.awaitAll()
|
||||
}
|
||||
notifyAllClients()
|
||||
}
|
||||
@@ -439,7 +445,9 @@ object DownloadManager {
|
||||
}
|
||||
}
|
||||
|
||||
enum class DownloaderState(val state: Int) {
|
||||
enum class DownloaderState(
|
||||
val state: Int,
|
||||
) {
|
||||
Stopped(0),
|
||||
Running(1),
|
||||
Paused(2),
|
||||
|
||||
@@ -69,16 +69,17 @@ class Downloader(
|
||||
fun start() {
|
||||
if (!isActive) {
|
||||
job =
|
||||
scope.launch {
|
||||
run()
|
||||
}.also { job ->
|
||||
job.invokeOnCompletion {
|
||||
if (it !is CancellationException) {
|
||||
logger.debug { "completed" }
|
||||
onComplete()
|
||||
scope
|
||||
.launch {
|
||||
run()
|
||||
}.also { job ->
|
||||
job.invokeOnCompletion {
|
||||
if (it !is CancellationException) {
|
||||
logger.debug { "completed" }
|
||||
onComplete()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
logger.debug { "started" }
|
||||
notifier(false)
|
||||
}
|
||||
|
||||
@@ -23,12 +23,16 @@ import java.io.File
|
||||
import java.io.InputStream
|
||||
|
||||
sealed class FileType {
|
||||
data class RegularFile(val file: File) : FileType()
|
||||
data class RegularFile(
|
||||
val file: File,
|
||||
) : FileType()
|
||||
|
||||
data class ZipFile(val entry: ZipArchiveEntry) : FileType()
|
||||
data class ZipFile(
|
||||
val entry: ZipArchiveEntry,
|
||||
) : FileType()
|
||||
|
||||
fun getName(): String {
|
||||
return when (this) {
|
||||
fun getName(): String =
|
||||
when (this) {
|
||||
is FileType.RegularFile -> {
|
||||
this.file.name
|
||||
}
|
||||
@@ -36,10 +40,9 @@ sealed class FileType {
|
||||
this.entry.name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getExtension(): String {
|
||||
return when (this) {
|
||||
fun getExtension(): String =
|
||||
when (this) {
|
||||
is FileType.RegularFile -> {
|
||||
this.file.extension
|
||||
}
|
||||
@@ -47,13 +50,15 @@ sealed class FileType {
|
||||
this.entry.name.substringAfterLast(".")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Base class for downloaded chapter files provider, example: Folder, Archive
|
||||
*/
|
||||
abstract class ChaptersFilesProvider<Type : FileType>(val mangaId: Int, val chapterId: Int) : DownloadedFilesProvider {
|
||||
abstract class ChaptersFilesProvider<Type : FileType>(
|
||||
val mangaId: Int,
|
||||
val chapterId: Int,
|
||||
) : DownloadedFilesProvider {
|
||||
protected abstract fun getImageFiles(): List<Type>
|
||||
|
||||
protected abstract fun getImageInputStream(image: Type): InputStream
|
||||
@@ -71,9 +76,7 @@ abstract class ChaptersFilesProvider<Type : FileType>(val mangaId: Int, val chap
|
||||
return Pair(getImageInputStream(image).buffered(), "image/$imageFileType")
|
||||
}
|
||||
|
||||
override fun getImage(): RetrieveFile1Args<Int> {
|
||||
return RetrieveFile1Args(::getImageImpl)
|
||||
}
|
||||
override fun getImage(): RetrieveFile1Args<Int> = RetrieveFile1Args(::getImageImpl)
|
||||
|
||||
/**
|
||||
* Extract the existing download to the base download folder (see [getChapterDownloadPath])
|
||||
@@ -110,21 +113,22 @@ abstract class ChaptersFilesProvider<Type : FileType>(val mangaId: Int, val chap
|
||||
}
|
||||
|
||||
try {
|
||||
Page.getPageImage(
|
||||
mangaId = download.mangaId,
|
||||
chapterIndex = download.chapterIndex,
|
||||
index = pageNum,
|
||||
) { flow ->
|
||||
pageProgressJob =
|
||||
flow
|
||||
.sample(100)
|
||||
.distinctUntilChanged()
|
||||
.onEach {
|
||||
download.progress = (pageNum.toFloat() + (it.toFloat() * 0.01f)) / pageCount
|
||||
step(null, false) // don't throw on canceled download here since we can't do anything
|
||||
}
|
||||
.launchIn(scope)
|
||||
}.first.close()
|
||||
Page
|
||||
.getPageImage(
|
||||
mangaId = download.mangaId,
|
||||
chapterIndex = download.chapterIndex,
|
||||
index = pageNum,
|
||||
) { flow ->
|
||||
pageProgressJob =
|
||||
flow
|
||||
.sample(100)
|
||||
.distinctUntilChanged()
|
||||
.onEach {
|
||||
download.progress = (pageNum.toFloat() + (it.toFloat() * 0.01f)) / pageCount
|
||||
step(null, false) // don't throw on canceled download here since we can't do anything
|
||||
}.launchIn(scope)
|
||||
}.first
|
||||
.close()
|
||||
} finally {
|
||||
// always cancel the page progress job even if it throws an exception to avoid memory leaks
|
||||
pageProgressJob?.cancel()
|
||||
@@ -151,9 +155,8 @@ abstract class ChaptersFilesProvider<Type : FileType>(val mangaId: Int, val chap
|
||||
return true
|
||||
}
|
||||
|
||||
override fun download(): FileDownload3Args<DownloadChapter, CoroutineScope, suspend (DownloadChapter?, Boolean) -> Unit> {
|
||||
return FileDownload3Args(::downloadImpl)
|
||||
}
|
||||
override fun download(): FileDownload3Args<DownloadChapter, CoroutineScope, suspend (DownloadChapter?, Boolean) -> Unit> =
|
||||
FileDownload3Args(::downloadImpl)
|
||||
|
||||
abstract override fun delete(): Boolean
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package suwayomi.tachidesk.manga.impl.download.fileProvider
|
||||
|
||||
interface DownloadedFilesProvider : FileDownloader, FileRetriever {
|
||||
interface DownloadedFilesProvider :
|
||||
FileDownloader,
|
||||
FileRetriever {
|
||||
fun delete(): Boolean
|
||||
}
|
||||
|
||||
@@ -7,9 +7,7 @@ fun interface FileDownload {
|
||||
fun interface FileDownload0Args : FileDownload {
|
||||
suspend fun execute(): Boolean
|
||||
|
||||
override suspend fun executeDownload(vararg args: Any): Boolean {
|
||||
return execute()
|
||||
}
|
||||
override suspend fun executeDownload(vararg args: Any): Boolean = execute()
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
@@ -20,9 +18,7 @@ fun interface FileDownload3Args<A, B, C> : FileDownload {
|
||||
c: C,
|
||||
): Boolean
|
||||
|
||||
override suspend fun executeDownload(vararg args: Any): Boolean {
|
||||
return execute(args[0] as A, args[1] as B, args[2] as C)
|
||||
}
|
||||
override suspend fun executeDownload(vararg args: Any): Boolean = execute(args[0] as A, args[1] as B, args[2] as C)
|
||||
}
|
||||
|
||||
fun interface FileDownloader {
|
||||
|
||||
@@ -9,18 +9,14 @@ fun interface RetrieveFile {
|
||||
fun interface RetrieveFile0Args : RetrieveFile {
|
||||
fun execute(): Pair<InputStream, String>
|
||||
|
||||
override fun executeGetImage(vararg args: Any): Pair<InputStream, String> {
|
||||
return execute()
|
||||
}
|
||||
override fun executeGetImage(vararg args: Any): Pair<InputStream, String> = execute()
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun interface RetrieveFile1Args<A> : RetrieveFile {
|
||||
fun execute(a: A): Pair<InputStream, String>
|
||||
|
||||
override fun executeGetImage(vararg args: Any): Pair<InputStream, String> {
|
||||
return execute(args[0] as A)
|
||||
}
|
||||
override fun executeGetImage(vararg args: Any): Pair<InputStream, String> = execute(args[0] as A)
|
||||
}
|
||||
|
||||
fun interface FileRetriever {
|
||||
|
||||
@@ -21,15 +21,21 @@ import java.io.InputStream
|
||||
|
||||
private val applicationDirs by DI.global.instance<ApplicationDirs>()
|
||||
|
||||
class ArchiveProvider(mangaId: Int, chapterId: Int) : ChaptersFilesProvider<FileType.ZipFile>(mangaId, chapterId) {
|
||||
class ArchiveProvider(
|
||||
mangaId: Int,
|
||||
chapterId: Int,
|
||||
) : ChaptersFilesProvider<FileType.ZipFile>(mangaId, chapterId) {
|
||||
override fun getImageFiles(): List<FileType.ZipFile> {
|
||||
val zipFile = ZipFile(getChapterCbzPath(mangaId, chapterId))
|
||||
val zipFile = ZipFile.builder().setFile(getChapterCbzPath(mangaId, chapterId)).get()
|
||||
return zipFile.entries.toList().map { FileType.ZipFile(it) }
|
||||
}
|
||||
|
||||
override fun getImageInputStream(image: FileType.ZipFile): InputStream {
|
||||
return ZipFile(getChapterCbzPath(mangaId, chapterId)).getInputStream(image.entry)
|
||||
}
|
||||
override fun getImageInputStream(image: FileType.ZipFile): InputStream =
|
||||
ZipFile
|
||||
.builder()
|
||||
.setFile(getChapterCbzPath(mangaId, chapterId))
|
||||
.get()
|
||||
.getInputStream(image.entry)
|
||||
|
||||
override fun extractExistingDownload() {
|
||||
val outputFile = File(getChapterCbzPath(mangaId, chapterId))
|
||||
|
||||
@@ -17,7 +17,10 @@ private val applicationDirs by DI.global.instance<ApplicationDirs>()
|
||||
/*
|
||||
* Provides downloaded files when pages were downloaded into folders
|
||||
* */
|
||||
class FolderProvider(mangaId: Int, chapterId: Int) : ChaptersFilesProvider<RegularFile>(mangaId, chapterId) {
|
||||
class FolderProvider(
|
||||
mangaId: Int,
|
||||
chapterId: Int,
|
||||
) : ChaptersFilesProvider<RegularFile>(mangaId, chapterId) {
|
||||
override fun getImageFiles(): List<RegularFile> {
|
||||
val chapterFolder = File(getChapterDownloadPath(mangaId, chapterId))
|
||||
|
||||
@@ -25,12 +28,14 @@ class FolderProvider(mangaId: Int, chapterId: Int) : ChaptersFilesProvider<Regul
|
||||
throw Exception("download folder does not exist")
|
||||
}
|
||||
|
||||
return chapterFolder.listFiles().orEmpty().toList().map(::RegularFile)
|
||||
return chapterFolder
|
||||
.listFiles()
|
||||
.orEmpty()
|
||||
.toList()
|
||||
.map(::RegularFile)
|
||||
}
|
||||
|
||||
override fun getImageInputStream(image: RegularFile): FileInputStream {
|
||||
return FileInputStream(image.file)
|
||||
}
|
||||
override fun getImageInputStream(image: RegularFile): FileInputStream = FileInputStream(image.file)
|
||||
|
||||
override fun extractExistingDownload() {
|
||||
// nothing to do
|
||||
|
||||
@@ -18,7 +18,9 @@ class MissingThumbnailException : Exception("No thumbnail found")
|
||||
|
||||
private val applicationDirs by DI.global.instance<ApplicationDirs>()
|
||||
|
||||
class ThumbnailFileProvider(val mangaId: Int) : DownloadedFilesProvider {
|
||||
class ThumbnailFileProvider(
|
||||
val mangaId: Int,
|
||||
) : DownloadedFilesProvider {
|
||||
private fun getFilePath(): String? {
|
||||
val thumbnailDir = applicationDirs.thumbnailDownloadsRoot
|
||||
val fileName = mangaId.toString()
|
||||
@@ -36,9 +38,7 @@ class ThumbnailFileProvider(val mangaId: Int) : DownloadedFilesProvider {
|
||||
return getCachedImageResponse(filePath, filePathWithoutExt)
|
||||
}
|
||||
|
||||
override fun getImage(): RetrieveFile0Args {
|
||||
return RetrieveFile0Args(::getImageImpl)
|
||||
}
|
||||
override fun getImage(): RetrieveFile0Args = RetrieveFile0Args(::getImageImpl)
|
||||
|
||||
private suspend fun downloadImpl(): Boolean {
|
||||
val isExistingFile = getFilePath() != null
|
||||
@@ -55,9 +55,7 @@ class ThumbnailFileProvider(val mangaId: Int) : DownloadedFilesProvider {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun download(): FileDownload0Args {
|
||||
return FileDownload0Args(::downloadImpl)
|
||||
}
|
||||
override fun download(): FileDownload0Args = FileDownload0Args(::downloadImpl)
|
||||
|
||||
override fun delete(): Boolean {
|
||||
val filePath = getFilePath()
|
||||
|
||||
@@ -20,7 +20,6 @@ class DownloadChapter(
|
||||
var progress: Float = 0f,
|
||||
var tries: Int = 0,
|
||||
) {
|
||||
override fun toString(): String {
|
||||
return "${manga.title} ($mangaId) - ${chapter.name} (${chapter.id}) | state= $state, tries= $tries, progress= $progress"
|
||||
}
|
||||
override fun toString(): String =
|
||||
"${manga.title} ($mangaId) - ${chapter.name} (${chapter.id}) | state= $state, tries= $tries, progress= $progress"
|
||||
}
|
||||
|
||||
@@ -7,7 +7,9 @@ package suwayomi.tachidesk.manga.impl.download.model
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
enum class DownloadState(val state: Int) {
|
||||
enum class DownloadState(
|
||||
val state: Int,
|
||||
) {
|
||||
Queued(0),
|
||||
Downloading(1),
|
||||
Finished(2),
|
||||
|
||||
@@ -78,8 +78,8 @@ object Extension {
|
||||
suspend fun installExternalExtension(
|
||||
inputStream: InputStream,
|
||||
apkName: String,
|
||||
): Int {
|
||||
return installAPK(true) {
|
||||
): Int =
|
||||
installAPK(true) {
|
||||
val savePath = "${applicationDirs.extensionsRoot}/$apkName"
|
||||
logger.debug { "Saving apk at $apkName" }
|
||||
// download apk file
|
||||
@@ -92,7 +92,6 @@ object Extension {
|
||||
}
|
||||
savePath
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun installAPK(
|
||||
forceReinstall: Boolean = false,
|
||||
@@ -174,7 +173,10 @@ object Extension {
|
||||
else -> "all"
|
||||
}
|
||||
|
||||
val extensionName = packageInfo.applicationInfo.nonLocalizedLabel.toString().substringAfter("Tachiyomi: ")
|
||||
val extensionName =
|
||||
packageInfo.applicationInfo.nonLocalizedLabel
|
||||
.toString()
|
||||
.substringAfter("Tachiyomi: ")
|
||||
|
||||
// update extension info
|
||||
transaction {
|
||||
@@ -277,9 +279,10 @@ object Extension {
|
||||
savePath: String,
|
||||
) {
|
||||
val response =
|
||||
network.client.newCall(
|
||||
GET(url, cache = CacheControl.FORCE_NETWORK),
|
||||
).await()
|
||||
network.client
|
||||
.newCall(
|
||||
GET(url, cache = CacheControl.FORCE_NETWORK),
|
||||
).await()
|
||||
|
||||
val downloadedFile = File(savePath)
|
||||
downloadedFile.sink().buffer().use { sink ->
|
||||
@@ -355,13 +358,12 @@ object Extension {
|
||||
val cacheSaveDir = "${applicationDirs.extensionsRoot}/icon"
|
||||
|
||||
return getImageResponse(cacheSaveDir, apkName) {
|
||||
network.client.newCall(
|
||||
GET(iconUrl, cache = CacheControl.FORCE_NETWORK),
|
||||
).await()
|
||||
network.client
|
||||
.newCall(
|
||||
GET(iconUrl, cache = CacheControl.FORCE_NETWORK),
|
||||
).await()
|
||||
}
|
||||
}
|
||||
|
||||
fun getExtensionIconUrl(apkName: String): String {
|
||||
return "/api/v1/extension/icon/$apkName"
|
||||
}
|
||||
fun getExtensionIconUrl(apkName: String): String = "/api/v1/extension/icon/$apkName"
|
||||
}
|
||||
|
||||
@@ -39,13 +39,14 @@ object ExtensionsList {
|
||||
// update if 60 seconds has passed or requested offline and database is empty
|
||||
val extensions =
|
||||
serverConfig.extensionRepos.value.map { repo ->
|
||||
kotlin.runCatching {
|
||||
ExtensionGithubApi.findExtensions(repo.repoUrlReplace())
|
||||
}.onFailure {
|
||||
logger.warn(it) {
|
||||
"Failed to fetch extensions for repo: $repo"
|
||||
kotlin
|
||||
.runCatching {
|
||||
ExtensionGithubApi.findExtensions(repo.repoUrlReplace())
|
||||
}.onFailure {
|
||||
logger.warn(it) {
|
||||
"Failed to fetch extensions for repo: $repo"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
val foundExtensions = extensions.mapNotNull { it.getOrNull() }.flatten()
|
||||
updateExtensionDatabase(foundExtensions)
|
||||
@@ -94,12 +95,15 @@ object ExtensionsList {
|
||||
updateExtensionDatabaseMutex.withLock {
|
||||
transaction {
|
||||
val uniqueExtensions =
|
||||
foundExtensions.groupBy { it.pkgName }.mapValues {
|
||||
(_, extension) ->
|
||||
extension.maxBy { it.versionCode }
|
||||
}.values
|
||||
foundExtensions
|
||||
.groupBy { it.pkgName }
|
||||
.mapValues { (_, extension) ->
|
||||
extension.maxBy { it.versionCode }
|
||||
}.values
|
||||
val installedExtensions =
|
||||
ExtensionTable.selectAll().toList()
|
||||
ExtensionTable
|
||||
.selectAll()
|
||||
.toList()
|
||||
.associateBy { it[ExtensionTable.pkgName] }
|
||||
val extensionsToUpdate = mutableListOf<Pair<OnlineExtension, ResultRow>>()
|
||||
val extensionsToInsert = mutableListOf<OnlineExtension>()
|
||||
@@ -189,7 +193,8 @@ object ExtensionsList {
|
||||
|
||||
// deal with obsolete extensions
|
||||
val extensionsToRemove =
|
||||
extensionsToDelete.groupBy { it[ExtensionTable.isInstalled] }
|
||||
extensionsToDelete
|
||||
.groupBy { it[ExtensionTable.isInstalled] }
|
||||
.mapValues { (_, extensions) -> extensions.map { it[ExtensionTable.pkgName] } }
|
||||
// not in the repo, so these extensions are obsolete
|
||||
val obsoleteExtensions = extensionsToRemove[true].orEmpty()
|
||||
@@ -207,8 +212,8 @@ object ExtensionsList {
|
||||
}
|
||||
}
|
||||
|
||||
private fun String.repoUrlReplace(): String {
|
||||
return if (contains("github")) {
|
||||
private fun String.repoUrlReplace(): String =
|
||||
if (contains("github")) {
|
||||
replace(repoMatchRegex) {
|
||||
"https://raw.githubusercontent.com/${it.groupValues[2]}/${it.groupValues[3]}/" +
|
||||
(it.groupValues.getOrNull(4)?.ifBlank { null } ?: "repo") +
|
||||
@@ -218,7 +223,6 @@ object ExtensionsList {
|
||||
} else {
|
||||
this
|
||||
}
|
||||
}
|
||||
|
||||
private val repoMatchRegex =
|
||||
(
|
||||
|
||||
@@ -58,29 +58,27 @@ object ExtensionGithubApi {
|
||||
fun getApkUrl(
|
||||
repo: String,
|
||||
apkName: String,
|
||||
): String {
|
||||
return "${repo}apk/$apkName"
|
||||
}
|
||||
): String = "${repo}apk/$apkName"
|
||||
|
||||
private val client by lazy {
|
||||
val network: NetworkHelper by injectLazy()
|
||||
network.client.newBuilder()
|
||||
network.client
|
||||
.newBuilder()
|
||||
.addNetworkInterceptor { chain ->
|
||||
val originalResponse = chain.proceed(chain.request())
|
||||
originalResponse.newBuilder()
|
||||
originalResponse
|
||||
.newBuilder()
|
||||
.header("Content-Type", "application/json")
|
||||
.build()
|
||||
}
|
||||
.build()
|
||||
}.build()
|
||||
}
|
||||
|
||||
private fun List<ExtensionJsonObject>.toExtensions(repo: String): List<OnlineExtension> {
|
||||
return this
|
||||
private fun List<ExtensionJsonObject>.toExtensions(repo: String): List<OnlineExtension> =
|
||||
this
|
||||
.filter {
|
||||
val libVersion = it.version.substringBeforeLast('.').toDouble()
|
||||
libVersion in LIB_VERSION_MIN..LIB_VERSION_MAX
|
||||
}
|
||||
.map {
|
||||
}.map {
|
||||
OnlineExtension(
|
||||
repo = repo,
|
||||
name = it.name.substringAfter("Tachiyomi: "),
|
||||
@@ -96,10 +94,9 @@ object ExtensionGithubApi {
|
||||
iconUrl = "${repo}icon/${it.pkg}.png",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun List<ExtensionSourceJsonObject>.toExtensionSources(): List<OnlineExtensionSource> {
|
||||
return this.map {
|
||||
private fun List<ExtensionSourceJsonObject>.toExtensionSources(): List<OnlineExtensionSource> =
|
||||
this.map {
|
||||
OnlineExtensionSource(
|
||||
name = it.name,
|
||||
lang = it.lang,
|
||||
@@ -107,5 +104,4 @@ object ExtensionGithubApi {
|
||||
baseUrl = it.baseUrl,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,9 +63,7 @@ object Track {
|
||||
tracker.logout()
|
||||
}
|
||||
|
||||
fun proxyThumbnailUrl(trackerId: Int): String {
|
||||
return "/api/v1/track/$trackerId/thumbnail"
|
||||
}
|
||||
fun proxyThumbnailUrl(trackerId: Int): String = "/api/v1/track/$trackerId/thumbnail"
|
||||
|
||||
fun getTrackerThumbnail(trackerId: Int): Pair<InputStream, String> {
|
||||
val tracker = TrackerManager.getTracker(trackerId)!!
|
||||
@@ -76,7 +74,8 @@ object Track {
|
||||
fun getTrackRecordsByMangaId(mangaId: Int): List<MangaTrackerDataClass> {
|
||||
val recordMap =
|
||||
transaction {
|
||||
TrackRecordTable.select { TrackRecordTable.mangaId eq mangaId }
|
||||
TrackRecordTable
|
||||
.select { TrackRecordTable.mangaId eq mangaId }
|
||||
.map { it.toTrackRecordDataClass() }
|
||||
}.associateBy { it.trackerId }
|
||||
|
||||
@@ -138,10 +137,12 @@ object Track {
|
||||
) {
|
||||
val track =
|
||||
transaction {
|
||||
TrackSearchTable.select {
|
||||
TrackSearchTable.trackerId eq trackerId and
|
||||
(TrackSearchTable.remoteId eq remoteId)
|
||||
}.first().toTrack(mangaId)
|
||||
TrackSearchTable
|
||||
.select {
|
||||
TrackSearchTable.trackerId eq trackerId and
|
||||
(TrackSearchTable.remoteId eq remoteId)
|
||||
}.first()
|
||||
.toTrack(mangaId)
|
||||
}
|
||||
val tracker = TrackerManager.getTracker(trackerId)!!
|
||||
|
||||
@@ -160,10 +161,10 @@ object Track {
|
||||
if (track.started_reading_date <= 0) {
|
||||
val oldestChapter =
|
||||
transaction {
|
||||
ChapterTable.select {
|
||||
(ChapterTable.manga eq mangaId) and (ChapterTable.isRead eq true)
|
||||
}
|
||||
.orderBy(ChapterTable.lastReadAt to SortOrder.ASC)
|
||||
ChapterTable
|
||||
.select {
|
||||
(ChapterTable.manga eq mangaId) and (ChapterTable.isRead eq true)
|
||||
}.orderBy(ChapterTable.lastReadAt to SortOrder.ASC)
|
||||
.limit(1)
|
||||
.firstOrNull()
|
||||
}
|
||||
@@ -290,14 +291,14 @@ object Track {
|
||||
}
|
||||
}
|
||||
|
||||
private fun queryMaxReadChapter(mangaId: Int): ResultRow? {
|
||||
return transaction {
|
||||
ChapterTable.select { (ChapterTable.manga eq mangaId) and (ChapterTable.isRead eq true) }
|
||||
private fun queryMaxReadChapter(mangaId: Int): ResultRow? =
|
||||
transaction {
|
||||
ChapterTable
|
||||
.select { (ChapterTable.manga eq mangaId) and (ChapterTable.isRead eq true) }
|
||||
.orderBy(ChapterTable.chapter_number to SortOrder.DESC)
|
||||
.limit(1)
|
||||
.firstOrNull()
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun trackChapter(
|
||||
mangaId: Int,
|
||||
@@ -305,7 +306,8 @@ object Track {
|
||||
) {
|
||||
val records =
|
||||
transaction {
|
||||
TrackRecordTable.select { TrackRecordTable.mangaId eq mangaId }
|
||||
TrackRecordTable
|
||||
.select { TrackRecordTable.mangaId eq mangaId }
|
||||
.toList()
|
||||
}
|
||||
|
||||
@@ -313,7 +315,8 @@ object Track {
|
||||
try {
|
||||
trackChapterForTracker(it, chapterNumber)
|
||||
} catch (e: Exception) {
|
||||
KotlinLogging.logger("${logger.name}::trackChapter(mangaId= $mangaId, chapterNumber= $chapterNumber)")
|
||||
KotlinLogging
|
||||
.logger("${logger.name}::trackChapter(mangaId= $mangaId, chapterNumber= $chapterNumber)")
|
||||
.error(e) { "failed due to" }
|
||||
}
|
||||
}
|
||||
@@ -358,14 +361,14 @@ object Track {
|
||||
}
|
||||
}
|
||||
|
||||
fun upsertTrackRecord(track: Track): Int {
|
||||
return transaction {
|
||||
fun upsertTrackRecord(track: Track): Int =
|
||||
transaction {
|
||||
val existingRecord =
|
||||
TrackRecordTable.select {
|
||||
(TrackRecordTable.mangaId eq track.manga_id) and
|
||||
(TrackRecordTable.trackerId eq track.sync_id)
|
||||
}
|
||||
.singleOrNull()
|
||||
TrackRecordTable
|
||||
.select {
|
||||
(TrackRecordTable.mangaId eq track.manga_id) and
|
||||
(TrackRecordTable.trackerId eq track.sync_id)
|
||||
}.singleOrNull()
|
||||
|
||||
if (existingRecord != null) {
|
||||
updateTrackRecord(track)
|
||||
@@ -374,7 +377,6 @@ object Track {
|
||||
insertTrackRecord(track)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun updateTrackRecord(track: Track): Int =
|
||||
transaction {
|
||||
@@ -399,20 +401,21 @@ object Track {
|
||||
|
||||
fun insertTrackRecord(track: Track): Int =
|
||||
transaction {
|
||||
TrackRecordTable.insertAndGetId {
|
||||
it[mangaId] = track.manga_id
|
||||
it[trackerId] = track.sync_id
|
||||
it[remoteId] = track.media_id
|
||||
it[libraryId] = track.library_id
|
||||
it[title] = track.title
|
||||
it[lastChapterRead] = track.last_chapter_read.toDouble()
|
||||
it[totalChapters] = track.total_chapters
|
||||
it[status] = track.status
|
||||
it[score] = track.score.toDouble()
|
||||
it[remoteUrl] = track.tracking_url
|
||||
it[startDate] = track.started_reading_date
|
||||
it[finishDate] = track.finished_reading_date
|
||||
}.value
|
||||
TrackRecordTable
|
||||
.insertAndGetId {
|
||||
it[mangaId] = track.manga_id
|
||||
it[trackerId] = track.sync_id
|
||||
it[remoteId] = track.media_id
|
||||
it[libraryId] = track.library_id
|
||||
it[title] = track.title
|
||||
it[lastChapterRead] = track.last_chapter_read.toDouble()
|
||||
it[totalChapters] = track.total_chapters
|
||||
it[status] = track.status
|
||||
it[score] = track.score.toDouble()
|
||||
it[remoteUrl] = track.tracking_url
|
||||
it[startDate] = track.started_reading_date
|
||||
it[finishDate] = track.finished_reading_date
|
||||
}.value
|
||||
}
|
||||
|
||||
@Serializable
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user