Add Source Meta (#875)

This commit is contained in:
Mitchell Syer
2024-02-17 11:24:01 -05:00
committed by GitHub
parent ea6edaecc4
commit af0dde5ae8
8 changed files with 181 additions and 0 deletions

View File

@@ -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() }
}
}
}
}

View File

@@ -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,

View File

@@ -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(),

View File

@@ -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,

View File

@@ -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")

View File

@@ -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
}
}
}
}
} }

View File

@@ -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")
}

View File

@@ -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(),
)
}