diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/TrackDataLoader.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/TrackDataLoader.kt index dd8d3f15..2af1cc06 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/TrackDataLoader.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/dataLoaders/TrackDataLoader.kt @@ -84,7 +84,7 @@ class TrackRecordsForTrackerIdDataLoader : KotlinDataLoader { - val (clientMutationId, mangaId, track) = input + val (clientMutationId, mangaId, trackSearchId) = input return future { Track.bind( mangaId, - TrackSearchDataClass( - syncId = track.syncId, - mediaId = track.mediaId, - title = track.title, - totalChapters = track.totalChapters, - trackingUrl = track.trackingUrl, - coverUrl = track.coverUrl, - summary = track.summary, - publishingStatus = track.publishingStatus, - publishingType = track.publishingType, - startDate = track.startDate, - ), + trackSearchId, ) val trackRecord = transaction { + val trackerId = + TrackSearchTable.select { TrackSearchTable.id eq trackSearchId } + .first()[TrackSearchTable.trackerId] TrackRecordTable.select { - TrackRecordTable.mangaId eq mangaId and (TrackRecordTable.syncId eq track.syncId) + TrackRecordTable.mangaId eq mangaId and (TrackRecordTable.trackerId eq trackerId) }.first() } BindTrackPayload( diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/TrackQuery.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/TrackQuery.kt index d4547de8..3a65e991 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/TrackQuery.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/TrackQuery.kt @@ -36,6 +36,7 @@ import suwayomi.tachidesk.graphql.types.TrackerNodeList import suwayomi.tachidesk.graphql.types.TrackerType import suwayomi.tachidesk.manga.impl.track.tracker.TrackerManager import suwayomi.tachidesk.manga.model.table.TrackRecordTable +import suwayomi.tachidesk.manga.model.table.insertAll import suwayomi.tachidesk.server.JavalinSetup.future import java.util.concurrent.CompletableFuture @@ -229,7 +230,7 @@ class TrackQuery { enum class TrackRecordOrderBy(override val column: Column>) : OrderBy { ID(TrackRecordTable.id), MANGA_ID(TrackRecordTable.mangaId), - SYNC_ID(TrackRecordTable.syncId), + TRACKER_ID(TrackRecordTable.trackerId), REMOTE_ID(TrackRecordTable.remoteId), TITLE(TrackRecordTable.title), LAST_CHAPTER_READ(TrackRecordTable.lastChapterRead), @@ -243,7 +244,7 @@ class TrackQuery { return when (this) { ID -> TrackRecordTable.id greater cursor.value.toInt() MANGA_ID -> greaterNotUnique(TrackRecordTable.mangaId, TrackRecordTable.id, cursor) - SYNC_ID -> greaterNotUnique(TrackRecordTable.syncId, TrackRecordTable.id, cursor, String::toInt) + TRACKER_ID -> greaterNotUnique(TrackRecordTable.trackerId, TrackRecordTable.id, cursor, String::toInt) REMOTE_ID -> greaterNotUnique(TrackRecordTable.remoteId, TrackRecordTable.id, cursor, String::toLong) TITLE -> greaterNotUnique(TrackRecordTable.title, TrackRecordTable.id, cursor, String::toString) LAST_CHAPTER_READ -> greaterNotUnique(TrackRecordTable.lastChapterRead, TrackRecordTable.id, cursor, String::toDouble) @@ -258,7 +259,7 @@ class TrackQuery { return when (this) { ID -> TrackRecordTable.id less cursor.value.toInt() MANGA_ID -> lessNotUnique(TrackRecordTable.mangaId, TrackRecordTable.id, cursor) - SYNC_ID -> lessNotUnique(TrackRecordTable.syncId, TrackRecordTable.id, cursor, String::toInt) + TRACKER_ID -> lessNotUnique(TrackRecordTable.trackerId, TrackRecordTable.id, cursor, String::toInt) REMOTE_ID -> lessNotUnique(TrackRecordTable.remoteId, TrackRecordTable.id, cursor, String::toLong) TITLE -> lessNotUnique(TrackRecordTable.title, TrackRecordTable.id, cursor, String::toString) LAST_CHAPTER_READ -> lessNotUnique(TrackRecordTable.lastChapterRead, TrackRecordTable.id, cursor, String::toDouble) @@ -274,7 +275,7 @@ class TrackQuery { when (this) { ID -> type.id.toString() MANGA_ID -> type.id.toString() + "-" + type.mangaId - SYNC_ID -> type.id.toString() + "-" + type.syncId + TRACKER_ID -> type.id.toString() + "-" + type.trackerId REMOTE_ID -> type.id.toString() + "-" + type.remoteId TITLE -> type.id.toString() + "-" + type.title LAST_CHAPTER_READ -> type.id.toString() + "-" + type.lastChapterRead @@ -290,7 +291,7 @@ class TrackQuery { data class TrackRecordCondition( val id: Int? = null, val mangaId: Int? = null, - val syncId: Int? = null, + val trackerId: Int? = null, val remoteId: Long? = null, val libraryId: Long? = null, val title: String? = null, @@ -306,7 +307,7 @@ class TrackQuery { val opAnd = OpAnd() opAnd.eq(id, TrackRecordTable.id) opAnd.eq(mangaId, TrackRecordTable.mangaId) - opAnd.eq(syncId, TrackRecordTable.syncId) + opAnd.eq(trackerId, TrackRecordTable.trackerId) opAnd.eq(remoteId, TrackRecordTable.remoteId) opAnd.eq(libraryId, TrackRecordTable.libraryId) opAnd.eq(title, TrackRecordTable.title) @@ -325,7 +326,7 @@ class TrackQuery { data class TrackRecordFilter( val id: IntFilter? = null, val mangaId: IntFilter? = null, - val syncId: IntFilter? = null, + val trackerId: IntFilter? = null, val remoteId: LongFilter? = null, val libraryId: LongFilter? = null, val title: StringFilter? = null, @@ -344,7 +345,7 @@ class TrackQuery { return listOfNotNull( andFilterWithCompareEntity(TrackRecordTable.id, id), andFilterWithCompareEntity(TrackRecordTable.mangaId, mangaId), - andFilterWithCompare(TrackRecordTable.syncId, syncId), + andFilterWithCompare(TrackRecordTable.trackerId, trackerId), andFilterWithCompare(TrackRecordTable.remoteId, remoteId), andFilterWithCompare(TrackRecordTable.libraryId, libraryId), andFilterWithCompareString(TrackRecordTable.title, title), @@ -462,7 +463,7 @@ class TrackQuery { "Tracker needs to be logged-in to search" } SearchTrackerPayload( - tracker.search(input.query).map { + tracker.search(input.query).insertAll().map { TrackSearchType(it) }, ) diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/TrackType.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/TrackType.kt index 1fa9bfc0..9c92c204 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/TrackType.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/TrackType.kt @@ -9,8 +9,8 @@ import suwayomi.tachidesk.graphql.server.primitives.Node import suwayomi.tachidesk.graphql.server.primitives.NodeList import suwayomi.tachidesk.graphql.server.primitives.PageInfo import suwayomi.tachidesk.manga.impl.track.tracker.Tracker -import suwayomi.tachidesk.manga.impl.track.tracker.model.TrackSearch import suwayomi.tachidesk.manga.model.table.TrackRecordTable +import suwayomi.tachidesk.manga.model.table.TrackSearchTable import java.util.concurrent.CompletableFuture class TrackerType( @@ -45,7 +45,7 @@ class TrackerType( class TrackRecordType( val id: Int, val mangaId: Int, - val syncId: Int, + val trackerId: Int, val remoteId: Long, val libraryId: Long?, val title: String, @@ -60,7 +60,7 @@ class TrackRecordType( constructor(row: ResultRow) : this( row[TrackRecordTable.id].value, row[TrackRecordTable.mangaId].value, - row[TrackRecordTable.syncId], + row[TrackRecordTable.trackerId], row[TrackRecordTable.remoteId], row[TrackRecordTable.libraryId], row[TrackRecordTable.title], @@ -82,13 +82,14 @@ class TrackRecordType( } fun tracker(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture { - return dataFetchingEnvironment.getValueFromDataLoader("TrackerDataLoader", syncId) + return dataFetchingEnvironment.getValueFromDataLoader("TrackerDataLoader", trackerId) } } class TrackSearchType( - val syncId: Int, - val mediaId: Long, + val id: Int, + val trackerId: Int, + val remoteId: Long, val title: String, val totalChapters: Int, val trackingUrl: String, @@ -98,21 +99,22 @@ class TrackSearchType( val publishingType: String, val startDate: String, ) { - constructor(trackSearch: TrackSearch) : this( - trackSearch.sync_id, - trackSearch.media_id, - trackSearch.title, - trackSearch.total_chapters, - trackSearch.tracking_url, - trackSearch.cover_url, - trackSearch.summary, - trackSearch.publishing_status, - trackSearch.publishing_type, - trackSearch.start_date, + constructor(row: ResultRow) : this( + row[TrackSearchTable.id].value, + row[TrackSearchTable.trackerId], + row[TrackSearchTable.remoteId], + row[TrackSearchTable.title], + row[TrackSearchTable.totalChapters], + row[TrackSearchTable.trackingUrl], + row[TrackSearchTable.coverUrl], + row[TrackSearchTable.summary], + row[TrackSearchTable.publishingStatus], + row[TrackSearchTable.publishingType], + row[TrackSearchTable.startDate], ) fun tracker(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture { - return dataFetchingEnvironment.getValueFromDataLoader("TrackerDataLoader", syncId) + return dataFetchingEnvironment.getValueFromDataLoader("TrackerDataLoader", trackerId) } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/controller/TrackController.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/controller/TrackController.kt index b0d1fa4a..5f65df42 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/controller/TrackController.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/controller/TrackController.kt @@ -14,7 +14,6 @@ import org.kodein.di.DI import org.kodein.di.conf.global import org.kodein.di.instance import suwayomi.tachidesk.manga.impl.track.Track -import suwayomi.tachidesk.manga.model.dataclass.TrackSearchDataClass import suwayomi.tachidesk.manga.model.dataclass.TrackerDataClass import suwayomi.tachidesk.server.JavalinSetup.future import suwayomi.tachidesk.server.util.handler @@ -104,17 +103,15 @@ object TrackController { val bind = handler( queryParam("mangaId"), + queryParam("trackSearchId"), documentWith = { withOperation { summary("Track Record Bind") description("Bind a Track Record to a Manga") } - body() }, - behaviorOf = { ctx, mangaId -> - val input = json.decodeFromString(ctx.body()) - logger.debug { "tracker bind $input" } - ctx.future(future { Track.bind(mangaId, input) }) + behaviorOf = { ctx, mangaId, trackSearchId -> + ctx.future(future { Track.bind(mangaId, trackSearchId) }) }, withResults = { httpCode(HttpCode.OK) diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/Track.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/Track.kt index a3b02325..2c5842f6 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/Track.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/Track.kt @@ -24,6 +24,8 @@ import suwayomi.tachidesk.manga.model.dataclass.TrackSearchDataClass import suwayomi.tachidesk.manga.model.dataclass.TrackerDataClass import suwayomi.tachidesk.manga.model.table.ChapterTable import suwayomi.tachidesk.manga.model.table.TrackRecordTable +import suwayomi.tachidesk.manga.model.table.TrackSearchTable +import suwayomi.tachidesk.manga.model.table.insertAll object Track { private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Default) @@ -66,7 +68,7 @@ object Track { transaction { TrackRecordTable.select { TrackRecordTable.mangaId eq mangaId } .map { it.toTrackRecordDataClass() } - }.associateBy { it.syncId } + }.associateBy { it.trackerId } val trackers = TrackerManager.services return trackers @@ -95,29 +97,42 @@ object Track { suspend fun search(input: SearchInput): List { val tracker = TrackerManager.getTracker(input.trackerId)!! val list = tracker.search(input.title) - return list.map { + return list.insertAll().map { TrackSearchDataClass( - syncId = it.sync_id, - mediaId = it.media_id, - title = it.title, - totalChapters = it.total_chapters, - trackingUrl = it.tracking_url, - coverUrl = it.cover_url, - summary = it.summary, - publishingStatus = it.publishing_status, - publishingType = it.publishing_type, - startDate = it.start_date, + id = it[TrackSearchTable.id].value, + trackerId = it[TrackSearchTable.trackerId], + remoteId = it[TrackSearchTable.remoteId], + title = it[TrackSearchTable.title], + totalChapters = it[TrackSearchTable.totalChapters], + trackingUrl = it[TrackSearchTable.trackingUrl], + coverUrl = it[TrackSearchTable.coverUrl], + summary = it[TrackSearchTable.summary], + publishingStatus = it[TrackSearchTable.publishingStatus], + publishingType = it[TrackSearchTable.publishingType], + startDate = it[TrackSearchTable.startDate], ) } } + private fun ResultRow.toTrack(mangaId: Int): Track = + Track.create(this[TrackSearchTable.trackerId]).also { + it.manga_id = mangaId + it.media_id = this[TrackSearchTable.remoteId] + it.title = this[TrackSearchTable.title] + it.total_chapters = this[TrackSearchTable.totalChapters] + it.tracking_url = this[TrackSearchTable.trackingUrl] + } + suspend fun bind( mangaId: Int, - input: TrackSearchDataClass, + trackSearchId: Int, ) { - val tracker = TrackerManager.getTracker(input.syncId)!! - - val track = input.toTrack(mangaId) + val track = + transaction { + TrackSearchTable.select { TrackSearchTable.id eq trackSearchId }.first() + .toTrack(mangaId) + } + val tracker = TrackerManager.getTracker(track.sync_id)!! val chapter = queryMaxReadChapter(mangaId) val hasReadChapters = chapter != null @@ -168,7 +183,7 @@ object Track { TrackRecordTable.select { TrackRecordTable.id eq input.recordId }.first() } - val tracker = TrackerManager.getTracker(recordDb[TrackRecordTable.syncId])!! + val tracker = TrackerManager.getTracker(recordDb[TrackRecordTable.trackerId])!! if (input.status != null) { recordDb[TrackRecordTable.status] = input.status @@ -250,7 +265,7 @@ object Track { } records.forEach { - val tracker = TrackerManager.getTracker(it[TrackRecordTable.syncId]) + val tracker = TrackerManager.getTracker(it[TrackRecordTable.trackerId]) val lastChapterRead = it[TrackRecordTable.lastChapterRead] val isLogin = tracker?.isLoggedIn == true logger.debug { @@ -271,14 +286,14 @@ object Track { val existingRecord = TrackRecordTable.select { (TrackRecordTable.mangaId eq track.manga_id) and - (TrackRecordTable.syncId eq track.sync_id) + (TrackRecordTable.trackerId eq track.sync_id) } .singleOrNull() if (existingRecord != null) { TrackRecordTable.update({ (TrackRecordTable.mangaId eq track.manga_id) and - (TrackRecordTable.syncId eq track.sync_id) + (TrackRecordTable.trackerId eq track.sync_id) }) { it[remoteId] = track.media_id it[libraryId] = track.library_id @@ -295,7 +310,7 @@ object Track { } else { TrackRecordTable.insertAndGetId { it[mangaId] = track.manga_id - it[syncId] = track.sync_id + it[trackerId] = track.sync_id it[remoteId] = track.media_id it[libraryId] = track.library_id it[title] = track.title diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/TrackerPreferences.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/TrackerPreferences.kt index c2a7b4de..7c97c88a 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/TrackerPreferences.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/TrackerPreferences.kt @@ -58,12 +58,12 @@ class TrackerPreferences { fun autoUpdateTrack() = preferenceStore.getBoolean("pref_auto_update_manga_sync_key", true) companion object { - fun trackUsername(syncId: Int) = "pref_mangasync_username_$syncId" + fun trackUsername(trackerId: Int) = "pref_mangasync_username_$trackerId" - private fun trackPassword(syncId: Int) = "pref_mangasync_password_$syncId" + private fun trackPassword(trackerId: Int) = "pref_mangasync_password_$trackerId" - private fun trackToken(syncId: Int) = "track_token_$syncId" + private fun trackToken(trackerId: Int) = "track_token_$trackerId" - private fun scoreType(syncId: Int) = "score_type_$syncId" + private fun scoreType(trackerId: Int) = "score_type_$trackerId" } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/model/TrackConvertor.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/model/TrackConvertor.kt index 144337cc..e2fc948d 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/model/TrackConvertor.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/model/TrackConvertor.kt @@ -2,23 +2,13 @@ package suwayomi.tachidesk.manga.impl.track.tracker.model import org.jetbrains.exposed.sql.ResultRow import suwayomi.tachidesk.manga.model.dataclass.TrackRecordDataClass -import suwayomi.tachidesk.manga.model.dataclass.TrackSearchDataClass import suwayomi.tachidesk.manga.model.table.TrackRecordTable -fun TrackSearchDataClass.toTrack(mangaId: Int): Track = - Track.create(syncId).also { - it.manga_id = mangaId - it.media_id = mediaId - it.title = title - it.total_chapters = totalChapters - it.tracking_url = trackingUrl - } - fun ResultRow.toTrackRecordDataClass(): TrackRecordDataClass = TrackRecordDataClass( id = this[TrackRecordTable.id].value, mangaId = this[TrackRecordTable.mangaId].value, - syncId = this[TrackRecordTable.syncId], + trackerId = this[TrackRecordTable.trackerId], remoteId = this[TrackRecordTable.remoteId], libraryId = this[TrackRecordTable.libraryId], title = this[TrackRecordTable.title], @@ -32,7 +22,7 @@ fun ResultRow.toTrackRecordDataClass(): TrackRecordDataClass = ) fun ResultRow.toTrack(): Track = - Track.create(this[TrackRecordTable.syncId]).also { + Track.create(this[TrackRecordTable.trackerId]).also { it.id = this[TrackRecordTable.id].value it.manga_id = this[TrackRecordTable.mangaId].value it.media_id = this[TrackRecordTable.remoteId] diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/model/dataclass/TrackRecordDataClass.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/dataclass/TrackRecordDataClass.kt index 4e4b287e..c1d9b448 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/model/dataclass/TrackRecordDataClass.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/dataclass/TrackRecordDataClass.kt @@ -10,7 +10,7 @@ package suwayomi.tachidesk.manga.model.dataclass data class TrackRecordDataClass( val id: Int, val mangaId: Int, - val syncId: Int, + val trackerId: Int, val remoteId: Long, val libraryId: Long?, val title: String, diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/model/dataclass/TrackSearchDataClass.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/dataclass/TrackSearchDataClass.kt index f6230fea..e2814cf6 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/model/dataclass/TrackSearchDataClass.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/dataclass/TrackSearchDataClass.kt @@ -11,8 +11,9 @@ import kotlinx.serialization.Serializable @Serializable data class TrackSearchDataClass( - val syncId: Int, - val mediaId: Long, + val id: Int, + val trackerId: Int, + val remoteId: Long, val title: String, val totalChapters: Int, val trackingUrl: String, diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/TrackRecordTable.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/TrackRecordTable.kt index 81e3e5ff..44903df7 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/TrackRecordTable.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/TrackRecordTable.kt @@ -12,7 +12,7 @@ import org.jetbrains.exposed.sql.ReferenceOption object TrackRecordTable : IntIdTable() { val mangaId = reference("manga_id", MangaTable, ReferenceOption.CASCADE) - val syncId = integer("sync_id") + val trackerId = integer("sync_id") val remoteId = long("remote_id") val libraryId = long("library_id").nullable() val title = varchar("title", 512) diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/TrackSearchTable.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/TrackSearchTable.kt new file mode 100644 index 00000000..9f2499d9 --- /dev/null +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/TrackSearchTable.kt @@ -0,0 +1,105 @@ +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.EntityID +import org.jetbrains.exposed.dao.id.IntIdTable +import org.jetbrains.exposed.sql.ResultRow +import org.jetbrains.exposed.sql.and +import org.jetbrains.exposed.sql.batchInsert +import org.jetbrains.exposed.sql.select +import org.jetbrains.exposed.sql.statements.BatchUpdateStatement +import org.jetbrains.exposed.sql.transactions.transaction +import suwayomi.tachidesk.manga.impl.track.tracker.model.TrackSearch + +object TrackSearchTable : IntIdTable() { + val trackerId = integer("tracker_id") + val remoteId = long("remote_id") + val title = varchar("title", 512) + val totalChapters = integer("total_chapters") + val trackingUrl = varchar("tracking_url", 512) + val coverUrl = varchar("cover_url", 512) + val summary = varchar("summary", 4096) + val publishingStatus = varchar("publishing_status", 512) + val publishingType = varchar("publishing_type", 512) + val startDate = varchar("start_date", 128) +} + +fun List.insertAll(): List { + if (isEmpty()) return emptyList() + return transaction { + val trackerIds = map { it.sync_id }.toSet() + val remoteIds = map { it.media_id }.toSet() + val existing = + transaction { + TrackSearchTable.select { + TrackSearchTable.trackerId inList trackerIds and (TrackSearchTable.remoteId inList remoteIds) + }.toList() + } + + val grouped = mutableMapOf>>() + forEach { trackSearch -> + val existingRow = + existing.find { + it[TrackSearchTable.trackerId] == trackSearch.sync_id && + it[TrackSearchTable.remoteId] == trackSearch.media_id + } + grouped.getOrPut(existingRow != null) { mutableListOf() } + .add(existingRow?.get(TrackSearchTable.id)?.value to trackSearch) + } + val toUpdate = grouped[true] + val toInsert = grouped[false]?.map { it.second } + if (!toUpdate.isNullOrEmpty()) { + BatchUpdateStatement(TrackSearchTable).apply { + toUpdate.forEach { (id, trackSearch) -> + id ?: return@forEach + addBatch(EntityID(id, TrackSearchTable)) + this[TrackSearchTable.title] = trackSearch.title.take(512) + this[TrackSearchTable.totalChapters] = trackSearch.total_chapters + this[TrackSearchTable.trackingUrl] = trackSearch.tracking_url.take(512) + this[TrackSearchTable.coverUrl] = trackSearch.cover_url.take(512) + this[TrackSearchTable.summary] = trackSearch.summary.take(4096) + this[TrackSearchTable.publishingStatus] = trackSearch.publishing_status.take(512) + this[TrackSearchTable.publishingType] = trackSearch.publishing_type.take(512) + this[TrackSearchTable.startDate] = trackSearch.start_date.take(128) + } + execute(this@transaction) + } + } + val insertedRows = + if (!toInsert.isNullOrEmpty()) { + TrackSearchTable.batchInsert(toInsert) { + this[TrackSearchTable.trackerId] = it.sync_id + this[TrackSearchTable.remoteId] = it.media_id + this[TrackSearchTable.title] = it.title.take(512) + this[TrackSearchTable.totalChapters] = it.total_chapters + this[TrackSearchTable.trackingUrl] = it.tracking_url.take(512) + this[TrackSearchTable.coverUrl] = it.cover_url.take(512) + this[TrackSearchTable.summary] = it.summary.take(4096) + this[TrackSearchTable.publishingStatus] = it.publishing_status.take(512) + this[TrackSearchTable.publishingType] = it.publishing_type.take(512) + this[TrackSearchTable.startDate] = it.start_date.take(128) + } + } else { + emptyList() + } + + val updatedRows = + toUpdate?.mapNotNull { it.first }?.let { ids -> + transaction { TrackSearchTable.select { TrackSearchTable.id inList ids }.toList() } + }.orEmpty() + + (insertedRows + updatedRows) + .sortedBy { row -> + indexOfFirst { + it.sync_id == row[TrackSearchTable.trackerId] && + it.media_id == row[TrackSearchTable.remoteId] + } + } + } +} diff --git a/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0035_TrackSearch.kt b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0035_TrackSearch.kt new file mode 100644 index 00000000..1aed1dfc --- /dev/null +++ b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0035_TrackSearch.kt @@ -0,0 +1,34 @@ +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 M0035_TrackSearch : AddTableMigration() { + private class TrackSearchTable : IntIdTable() { + val trackerId = integer("tracker_id") + val remoteId = long("remote_id") + val title = varchar("title", 512) + val totalChapters = integer("total_chapters") + val trackingUrl = varchar("tracking_url", 512) + val coverUrl = varchar("cover_url", 512) + val summary = varchar("summary", 4096) + val publishingStatus = varchar("publishing_status", 512) + val publishingType = varchar("publishing_type", 512) + val startDate = varchar("start_date", 128) + } + + override val tables: Array + get() = + arrayOf( + TrackSearchTable(), + ) +}