mirror of
https://github.com/Suwayomi/Tachidesk.git
synced 2025-12-10 14:52:05 +01:00
Add Source Meta (#875)
This commit is contained in:
@@ -12,9 +12,11 @@ import suwayomi.tachidesk.graphql.types.CategoryMetaType
|
|||||||
import suwayomi.tachidesk.graphql.types.ChapterMetaType
|
import suwayomi.tachidesk.graphql.types.ChapterMetaType
|
||||||
import suwayomi.tachidesk.graphql.types.GlobalMetaType
|
import suwayomi.tachidesk.graphql.types.GlobalMetaType
|
||||||
import suwayomi.tachidesk.graphql.types.MangaMetaType
|
import suwayomi.tachidesk.graphql.types.MangaMetaType
|
||||||
|
import suwayomi.tachidesk.graphql.types.SourceMetaType
|
||||||
import suwayomi.tachidesk.manga.model.table.CategoryMetaTable
|
import suwayomi.tachidesk.manga.model.table.CategoryMetaTable
|
||||||
import suwayomi.tachidesk.manga.model.table.ChapterMetaTable
|
import suwayomi.tachidesk.manga.model.table.ChapterMetaTable
|
||||||
import suwayomi.tachidesk.manga.model.table.MangaMetaTable
|
import suwayomi.tachidesk.manga.model.table.MangaMetaTable
|
||||||
|
import suwayomi.tachidesk.manga.model.table.SourceMetaTable
|
||||||
import suwayomi.tachidesk.server.JavalinSetup.future
|
import suwayomi.tachidesk.server.JavalinSetup.future
|
||||||
|
|
||||||
class GlobalMetaDataLoader : KotlinDataLoader<String, GlobalMetaType?> {
|
class GlobalMetaDataLoader : KotlinDataLoader<String, GlobalMetaType?> {
|
||||||
@@ -88,3 +90,21 @@ class CategoryMetaDataLoader : KotlinDataLoader<Int, List<CategoryMetaType>> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class SourceMetaDataLoader : KotlinDataLoader<Long, List<SourceMetaType>> {
|
||||||
|
override val dataLoaderName = "SourceMetaDataLoader"
|
||||||
|
|
||||||
|
override fun getDataLoader(): DataLoader<Long, List<SourceMetaType>> =
|
||||||
|
DataLoaderFactory.newDataLoader<Long, List<SourceMetaType>> { ids ->
|
||||||
|
future {
|
||||||
|
transaction {
|
||||||
|
addLogger(Slf4jSqlDebugLogger)
|
||||||
|
val metasByRefId =
|
||||||
|
SourceMetaTable.select { SourceMetaTable.ref inList ids }
|
||||||
|
.map { SourceMetaType(it) }
|
||||||
|
.groupBy { it.sourceId }
|
||||||
|
ids.map { metasByRefId[it].orEmpty() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,11 +5,15 @@ import androidx.preference.EditTextPreference
|
|||||||
import androidx.preference.ListPreference
|
import androidx.preference.ListPreference
|
||||||
import androidx.preference.MultiSelectListPreference
|
import androidx.preference.MultiSelectListPreference
|
||||||
import androidx.preference.SwitchPreferenceCompat
|
import androidx.preference.SwitchPreferenceCompat
|
||||||
|
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||||
|
import org.jetbrains.exposed.sql.and
|
||||||
|
import org.jetbrains.exposed.sql.deleteWhere
|
||||||
import org.jetbrains.exposed.sql.select
|
import org.jetbrains.exposed.sql.select
|
||||||
import org.jetbrains.exposed.sql.transactions.transaction
|
import org.jetbrains.exposed.sql.transactions.transaction
|
||||||
import suwayomi.tachidesk.graphql.types.FilterChange
|
import suwayomi.tachidesk.graphql.types.FilterChange
|
||||||
import suwayomi.tachidesk.graphql.types.MangaType
|
import suwayomi.tachidesk.graphql.types.MangaType
|
||||||
import suwayomi.tachidesk.graphql.types.Preference
|
import suwayomi.tachidesk.graphql.types.Preference
|
||||||
|
import suwayomi.tachidesk.graphql.types.SourceMetaType
|
||||||
import suwayomi.tachidesk.graphql.types.SourceType
|
import suwayomi.tachidesk.graphql.types.SourceType
|
||||||
import suwayomi.tachidesk.graphql.types.preferenceOf
|
import suwayomi.tachidesk.graphql.types.preferenceOf
|
||||||
import suwayomi.tachidesk.graphql.types.updateFilterList
|
import suwayomi.tachidesk.graphql.types.updateFilterList
|
||||||
@@ -17,11 +21,69 @@ import suwayomi.tachidesk.manga.impl.MangaList.insertOrGet
|
|||||||
import suwayomi.tachidesk.manga.impl.Source
|
import suwayomi.tachidesk.manga.impl.Source
|
||||||
import suwayomi.tachidesk.manga.impl.util.source.GetCatalogueSource
|
import suwayomi.tachidesk.manga.impl.util.source.GetCatalogueSource
|
||||||
import suwayomi.tachidesk.manga.model.table.MangaTable
|
import suwayomi.tachidesk.manga.model.table.MangaTable
|
||||||
|
import suwayomi.tachidesk.manga.model.table.SourceMetaTable
|
||||||
import suwayomi.tachidesk.manga.model.table.SourceTable
|
import suwayomi.tachidesk.manga.model.table.SourceTable
|
||||||
import suwayomi.tachidesk.server.JavalinSetup.future
|
import suwayomi.tachidesk.server.JavalinSetup.future
|
||||||
import java.util.concurrent.CompletableFuture
|
import java.util.concurrent.CompletableFuture
|
||||||
|
|
||||||
class SourceMutation {
|
class SourceMutation {
|
||||||
|
data class SetSourceMetaInput(
|
||||||
|
val clientMutationId: String? = null,
|
||||||
|
val meta: SourceMetaType,
|
||||||
|
)
|
||||||
|
|
||||||
|
data class SetSourceMetaPayload(
|
||||||
|
val clientMutationId: String?,
|
||||||
|
val meta: SourceMetaType,
|
||||||
|
)
|
||||||
|
|
||||||
|
fun setSourceMeta(input: SetSourceMetaInput): SetSourceMetaPayload {
|
||||||
|
val (clientMutationId, meta) = input
|
||||||
|
|
||||||
|
Source.modifyMeta(meta.sourceId, meta.key, meta.value)
|
||||||
|
|
||||||
|
return SetSourceMetaPayload(clientMutationId, meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
data class DeleteSourceMetaInput(
|
||||||
|
val clientMutationId: String? = null,
|
||||||
|
val sourceId: Long,
|
||||||
|
val key: String,
|
||||||
|
)
|
||||||
|
|
||||||
|
data class DeleteSourceMetaPayload(
|
||||||
|
val clientMutationId: String?,
|
||||||
|
val meta: SourceMetaType?,
|
||||||
|
val source: SourceType?,
|
||||||
|
)
|
||||||
|
|
||||||
|
fun deleteSourceMeta(input: DeleteSourceMetaInput): DeleteSourceMetaPayload {
|
||||||
|
val (clientMutationId, sourceId, key) = input
|
||||||
|
|
||||||
|
val (meta, source) =
|
||||||
|
transaction {
|
||||||
|
val meta =
|
||||||
|
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()
|
||||||
|
?.let { SourceType(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (meta != null) {
|
||||||
|
SourceMetaType(meta)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
} to source
|
||||||
|
}
|
||||||
|
|
||||||
|
return DeleteSourceMetaPayload(clientMutationId, meta, source)
|
||||||
|
}
|
||||||
|
|
||||||
enum class FetchSourceMangaType {
|
enum class FetchSourceMangaType {
|
||||||
SEARCH,
|
SEARCH,
|
||||||
POPULAR,
|
POPULAR,
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import suwayomi.tachidesk.graphql.dataLoaders.MangaForIdsDataLoader
|
|||||||
import suwayomi.tachidesk.graphql.dataLoaders.MangaForSourceDataLoader
|
import suwayomi.tachidesk.graphql.dataLoaders.MangaForSourceDataLoader
|
||||||
import suwayomi.tachidesk.graphql.dataLoaders.MangaMetaDataLoader
|
import suwayomi.tachidesk.graphql.dataLoaders.MangaMetaDataLoader
|
||||||
import suwayomi.tachidesk.graphql.dataLoaders.SourceDataLoader
|
import suwayomi.tachidesk.graphql.dataLoaders.SourceDataLoader
|
||||||
|
import suwayomi.tachidesk.graphql.dataLoaders.SourceMetaDataLoader
|
||||||
import suwayomi.tachidesk.graphql.dataLoaders.SourcesForExtensionDataLoader
|
import suwayomi.tachidesk.graphql.dataLoaders.SourcesForExtensionDataLoader
|
||||||
import suwayomi.tachidesk.graphql.dataLoaders.TrackRecordDataLoader
|
import suwayomi.tachidesk.graphql.dataLoaders.TrackRecordDataLoader
|
||||||
import suwayomi.tachidesk.graphql.dataLoaders.TrackRecordsForMangaIdDataLoader
|
import suwayomi.tachidesk.graphql.dataLoaders.TrackRecordsForMangaIdDataLoader
|
||||||
@@ -64,6 +65,7 @@ class TachideskDataLoaderRegistryFactory {
|
|||||||
CategoriesForMangaDataLoader(),
|
CategoriesForMangaDataLoader(),
|
||||||
SourceDataLoader(),
|
SourceDataLoader(),
|
||||||
SourcesForExtensionDataLoader(),
|
SourcesForExtensionDataLoader(),
|
||||||
|
SourceMetaDataLoader(),
|
||||||
ExtensionDataLoader(),
|
ExtensionDataLoader(),
|
||||||
ExtensionForSourceDataLoader(),
|
ExtensionForSourceDataLoader(),
|
||||||
TrackerDataLoader(),
|
TrackerDataLoader(),
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import suwayomi.tachidesk.graphql.server.primitives.PageInfo
|
|||||||
import suwayomi.tachidesk.manga.model.table.CategoryMetaTable
|
import suwayomi.tachidesk.manga.model.table.CategoryMetaTable
|
||||||
import suwayomi.tachidesk.manga.model.table.ChapterMetaTable
|
import suwayomi.tachidesk.manga.model.table.ChapterMetaTable
|
||||||
import suwayomi.tachidesk.manga.model.table.MangaMetaTable
|
import suwayomi.tachidesk.manga.model.table.MangaMetaTable
|
||||||
|
import suwayomi.tachidesk.manga.model.table.SourceMetaTable
|
||||||
import java.util.concurrent.CompletableFuture
|
import java.util.concurrent.CompletableFuture
|
||||||
|
|
||||||
interface MetaType : Node {
|
interface MetaType : Node {
|
||||||
@@ -67,6 +68,22 @@ class CategoryMetaType(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class SourceMetaType(
|
||||||
|
override val key: String,
|
||||||
|
override val value: String,
|
||||||
|
val sourceId: Long,
|
||||||
|
) : MetaType {
|
||||||
|
constructor(row: ResultRow) : this(
|
||||||
|
key = row[SourceMetaTable.key],
|
||||||
|
value = row[SourceMetaTable.value],
|
||||||
|
sourceId = row[SourceMetaTable.ref],
|
||||||
|
)
|
||||||
|
|
||||||
|
fun source(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<SourceType> {
|
||||||
|
return dataFetchingEnvironment.getValueFromDataLoader<Long, SourceType>("SourceDataLoader", sourceId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class GlobalMetaType(
|
class GlobalMetaType(
|
||||||
override val key: String,
|
override val key: String,
|
||||||
override val value: String,
|
override val value: String,
|
||||||
|
|||||||
@@ -82,6 +82,10 @@ class SourceType(
|
|||||||
fun filters(): List<Filter> {
|
fun filters(): List<Filter> {
|
||||||
return getCatalogueSourceOrStub(id).getFilterList().map { filterOf(it) }
|
return getCatalogueSourceOrStub(id).getFilterList().map { filterOf(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun meta(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<List<SourceMetaType>> {
|
||||||
|
return dataFetchingEnvironment.getValueFromDataLoader<Long, List<SourceMetaType>>("SourceMetaDataLoader", id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("ktlint:standard:function-naming")
|
@Suppress("ktlint:standard:function-naming")
|
||||||
|
|||||||
@@ -13,9 +13,12 @@ import eu.kanade.tachiyomi.source.ConfigurableSource
|
|||||||
import eu.kanade.tachiyomi.source.sourcePreferences
|
import eu.kanade.tachiyomi.source.sourcePreferences
|
||||||
import io.javalin.plugin.json.JsonMapper
|
import io.javalin.plugin.json.JsonMapper
|
||||||
import mu.KotlinLogging
|
import mu.KotlinLogging
|
||||||
|
import org.jetbrains.exposed.sql.and
|
||||||
|
import org.jetbrains.exposed.sql.insert
|
||||||
import org.jetbrains.exposed.sql.select
|
import org.jetbrains.exposed.sql.select
|
||||||
import org.jetbrains.exposed.sql.selectAll
|
import org.jetbrains.exposed.sql.selectAll
|
||||||
import org.jetbrains.exposed.sql.transactions.transaction
|
import org.jetbrains.exposed.sql.transactions.transaction
|
||||||
|
import org.jetbrains.exposed.sql.update
|
||||||
import org.kodein.di.DI
|
import org.kodein.di.DI
|
||||||
import org.kodein.di.conf.global
|
import org.kodein.di.conf.global
|
||||||
import org.kodein.di.instance
|
import org.kodein.di.instance
|
||||||
@@ -25,6 +28,7 @@ import suwayomi.tachidesk.manga.impl.util.source.GetCatalogueSource.getCatalogue
|
|||||||
import suwayomi.tachidesk.manga.impl.util.source.GetCatalogueSource.unregisterCatalogueSource
|
import suwayomi.tachidesk.manga.impl.util.source.GetCatalogueSource.unregisterCatalogueSource
|
||||||
import suwayomi.tachidesk.manga.model.dataclass.SourceDataClass
|
import suwayomi.tachidesk.manga.model.dataclass.SourceDataClass
|
||||||
import suwayomi.tachidesk.manga.model.table.ExtensionTable
|
import suwayomi.tachidesk.manga.model.table.ExtensionTable
|
||||||
|
import suwayomi.tachidesk.manga.model.table.SourceMetaTable
|
||||||
import suwayomi.tachidesk.manga.model.table.SourceTable
|
import suwayomi.tachidesk.manga.model.table.SourceTable
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
import xyz.nulldev.androidcompat.androidimpl.CustomContext
|
import xyz.nulldev.androidcompat.androidimpl.CustomContext
|
||||||
@@ -150,4 +154,29 @@ object Source {
|
|||||||
// must reload the source because a preference was changed
|
// must reload the source because a preference was changed
|
||||||
unregisterCatalogueSource(sourceId)
|
unregisterCatalogueSource(sourceId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun modifyMeta(
|
||||||
|
sourceId: Long,
|
||||||
|
key: String,
|
||||||
|
value: String,
|
||||||
|
) {
|
||||||
|
transaction {
|
||||||
|
val meta =
|
||||||
|
transaction {
|
||||||
|
SourceMetaTable.select { (SourceMetaTable.ref eq sourceId) and (SourceMetaTable.key eq key) }
|
||||||
|
}.firstOrNull()
|
||||||
|
|
||||||
|
if (meta == null) {
|
||||||
|
SourceMetaTable.insert {
|
||||||
|
it[SourceMetaTable.key] = key
|
||||||
|
it[SourceMetaTable.value] = value
|
||||||
|
it[SourceMetaTable.ref] = sourceId
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
SourceMetaTable.update({ (SourceMetaTable.ref eq sourceId) and (SourceMetaTable.key eq key) }) {
|
||||||
|
it[SourceMetaTable.value] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package suwayomi.tachidesk.manga.model.table
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) Contributors to the Suwayomi project
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
import org.jetbrains.exposed.dao.id.IntIdTable
|
||||||
|
import suwayomi.tachidesk.manga.model.table.ChapterMetaTable.ref
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Metadata storage for clients, about Source with id == [ref].
|
||||||
|
*/
|
||||||
|
object SourceMetaTable : IntIdTable() {
|
||||||
|
val key = varchar("key", 256)
|
||||||
|
val value = varchar("value", 4096)
|
||||||
|
val ref = long("source_ref")
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package suwayomi.tachidesk.server.database.migration
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) Contributors to the Suwayomi project
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
import de.neonew.exposed.migrations.helpers.AddTableMigration
|
||||||
|
import org.jetbrains.exposed.dao.id.IntIdTable
|
||||||
|
import org.jetbrains.exposed.sql.Table
|
||||||
|
|
||||||
|
@Suppress("ClassName", "unused")
|
||||||
|
class M0036_SourceMeta : AddTableMigration() {
|
||||||
|
private class SourceMetaTable : IntIdTable() {
|
||||||
|
val key = varchar("key", 256)
|
||||||
|
val value = varchar("value", 4096)
|
||||||
|
val ref = long("source_ref")
|
||||||
|
}
|
||||||
|
|
||||||
|
override val tables: Array<Table>
|
||||||
|
get() =
|
||||||
|
arrayOf(
|
||||||
|
SourceMetaTable(),
|
||||||
|
)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user