mirror of
https://github.com/Suwayomi/Suwayomi-Server.git
synced 2026-07-03 02:44:34 -05:00
@@ -84,7 +84,7 @@ class TrackRecordsForTrackerIdDataLoader : KotlinDataLoader<Int, TrackRecordNode
|
|||||||
transaction {
|
transaction {
|
||||||
addLogger(Slf4jSqlDebugLogger)
|
addLogger(Slf4jSqlDebugLogger)
|
||||||
val trackRecordsBySyncId =
|
val trackRecordsBySyncId =
|
||||||
TrackRecordTable.select { TrackRecordTable.syncId inList ids }
|
TrackRecordTable.select { TrackRecordTable.trackerId inList ids }
|
||||||
.map { TrackRecordType(it) }
|
.map { TrackRecordType(it) }
|
||||||
.groupBy { it.mangaId }
|
.groupBy { it.mangaId }
|
||||||
ids.map { (trackRecordsBySyncId[it] ?: emptyList()).toNodeList() }
|
ids.map { (trackRecordsBySyncId[it] ?: emptyList()).toNodeList() }
|
||||||
|
|||||||
@@ -4,12 +4,11 @@ import org.jetbrains.exposed.sql.and
|
|||||||
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.TrackRecordType
|
import suwayomi.tachidesk.graphql.types.TrackRecordType
|
||||||
import suwayomi.tachidesk.graphql.types.TrackSearchType
|
|
||||||
import suwayomi.tachidesk.graphql.types.TrackerType
|
import suwayomi.tachidesk.graphql.types.TrackerType
|
||||||
import suwayomi.tachidesk.manga.impl.track.Track
|
import suwayomi.tachidesk.manga.impl.track.Track
|
||||||
import suwayomi.tachidesk.manga.impl.track.tracker.TrackerManager
|
import suwayomi.tachidesk.manga.impl.track.tracker.TrackerManager
|
||||||
import suwayomi.tachidesk.manga.model.dataclass.TrackSearchDataClass
|
|
||||||
import suwayomi.tachidesk.manga.model.table.TrackRecordTable
|
import suwayomi.tachidesk.manga.model.table.TrackRecordTable
|
||||||
|
import suwayomi.tachidesk.manga.model.table.TrackSearchTable
|
||||||
import suwayomi.tachidesk.server.JavalinSetup.future
|
import suwayomi.tachidesk.server.JavalinSetup.future
|
||||||
import java.util.concurrent.CompletableFuture
|
import java.util.concurrent.CompletableFuture
|
||||||
|
|
||||||
@@ -104,7 +103,7 @@ class TrackMutation {
|
|||||||
data class BindTrackInput(
|
data class BindTrackInput(
|
||||||
val clientMutationId: String? = null,
|
val clientMutationId: String? = null,
|
||||||
val mangaId: Int,
|
val mangaId: Int,
|
||||||
val track: TrackSearchType,
|
val trackSearchId: Int,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class BindTrackPayload(
|
data class BindTrackPayload(
|
||||||
@@ -113,28 +112,20 @@ class TrackMutation {
|
|||||||
)
|
)
|
||||||
|
|
||||||
fun bindTrack(input: BindTrackInput): CompletableFuture<BindTrackPayload> {
|
fun bindTrack(input: BindTrackInput): CompletableFuture<BindTrackPayload> {
|
||||||
val (clientMutationId, mangaId, track) = input
|
val (clientMutationId, mangaId, trackSearchId) = input
|
||||||
|
|
||||||
return future {
|
return future {
|
||||||
Track.bind(
|
Track.bind(
|
||||||
mangaId,
|
mangaId,
|
||||||
TrackSearchDataClass(
|
trackSearchId,
|
||||||
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,
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
val trackRecord =
|
val trackRecord =
|
||||||
transaction {
|
transaction {
|
||||||
|
val trackerId =
|
||||||
|
TrackSearchTable.select { TrackSearchTable.id eq trackSearchId }
|
||||||
|
.first()[TrackSearchTable.trackerId]
|
||||||
TrackRecordTable.select {
|
TrackRecordTable.select {
|
||||||
TrackRecordTable.mangaId eq mangaId and (TrackRecordTable.syncId eq track.syncId)
|
TrackRecordTable.mangaId eq mangaId and (TrackRecordTable.trackerId eq trackerId)
|
||||||
}.first()
|
}.first()
|
||||||
}
|
}
|
||||||
BindTrackPayload(
|
BindTrackPayload(
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ import suwayomi.tachidesk.graphql.types.TrackerNodeList
|
|||||||
import suwayomi.tachidesk.graphql.types.TrackerType
|
import suwayomi.tachidesk.graphql.types.TrackerType
|
||||||
import suwayomi.tachidesk.manga.impl.track.tracker.TrackerManager
|
import suwayomi.tachidesk.manga.impl.track.tracker.TrackerManager
|
||||||
import suwayomi.tachidesk.manga.model.table.TrackRecordTable
|
import suwayomi.tachidesk.manga.model.table.TrackRecordTable
|
||||||
|
import suwayomi.tachidesk.manga.model.table.insertAll
|
||||||
import suwayomi.tachidesk.server.JavalinSetup.future
|
import suwayomi.tachidesk.server.JavalinSetup.future
|
||||||
import java.util.concurrent.CompletableFuture
|
import java.util.concurrent.CompletableFuture
|
||||||
|
|
||||||
@@ -229,7 +230,7 @@ class TrackQuery {
|
|||||||
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),
|
ID(TrackRecordTable.id),
|
||||||
MANGA_ID(TrackRecordTable.mangaId),
|
MANGA_ID(TrackRecordTable.mangaId),
|
||||||
SYNC_ID(TrackRecordTable.syncId),
|
TRACKER_ID(TrackRecordTable.trackerId),
|
||||||
REMOTE_ID(TrackRecordTable.remoteId),
|
REMOTE_ID(TrackRecordTable.remoteId),
|
||||||
TITLE(TrackRecordTable.title),
|
TITLE(TrackRecordTable.title),
|
||||||
LAST_CHAPTER_READ(TrackRecordTable.lastChapterRead),
|
LAST_CHAPTER_READ(TrackRecordTable.lastChapterRead),
|
||||||
@@ -243,7 +244,7 @@ class TrackQuery {
|
|||||||
return when (this) {
|
return when (this) {
|
||||||
ID -> TrackRecordTable.id greater cursor.value.toInt()
|
ID -> TrackRecordTable.id greater cursor.value.toInt()
|
||||||
MANGA_ID -> greaterNotUnique(TrackRecordTable.mangaId, TrackRecordTable.id, cursor)
|
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)
|
REMOTE_ID -> greaterNotUnique(TrackRecordTable.remoteId, TrackRecordTable.id, cursor, String::toLong)
|
||||||
TITLE -> greaterNotUnique(TrackRecordTable.title, TrackRecordTable.id, cursor, String::toString)
|
TITLE -> greaterNotUnique(TrackRecordTable.title, TrackRecordTable.id, cursor, String::toString)
|
||||||
LAST_CHAPTER_READ -> greaterNotUnique(TrackRecordTable.lastChapterRead, TrackRecordTable.id, cursor, String::toDouble)
|
LAST_CHAPTER_READ -> greaterNotUnique(TrackRecordTable.lastChapterRead, TrackRecordTable.id, cursor, String::toDouble)
|
||||||
@@ -258,7 +259,7 @@ class TrackQuery {
|
|||||||
return when (this) {
|
return when (this) {
|
||||||
ID -> TrackRecordTable.id less cursor.value.toInt()
|
ID -> TrackRecordTable.id less cursor.value.toInt()
|
||||||
MANGA_ID -> lessNotUnique(TrackRecordTable.mangaId, TrackRecordTable.id, cursor)
|
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)
|
REMOTE_ID -> lessNotUnique(TrackRecordTable.remoteId, TrackRecordTable.id, cursor, String::toLong)
|
||||||
TITLE -> lessNotUnique(TrackRecordTable.title, TrackRecordTable.id, cursor, String::toString)
|
TITLE -> lessNotUnique(TrackRecordTable.title, TrackRecordTable.id, cursor, String::toString)
|
||||||
LAST_CHAPTER_READ -> lessNotUnique(TrackRecordTable.lastChapterRead, TrackRecordTable.id, cursor, String::toDouble)
|
LAST_CHAPTER_READ -> lessNotUnique(TrackRecordTable.lastChapterRead, TrackRecordTable.id, cursor, String::toDouble)
|
||||||
@@ -274,7 +275,7 @@ class TrackQuery {
|
|||||||
when (this) {
|
when (this) {
|
||||||
ID -> type.id.toString()
|
ID -> type.id.toString()
|
||||||
MANGA_ID -> type.id.toString() + "-" + type.mangaId
|
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
|
REMOTE_ID -> type.id.toString() + "-" + type.remoteId
|
||||||
TITLE -> type.id.toString() + "-" + type.title
|
TITLE -> type.id.toString() + "-" + type.title
|
||||||
LAST_CHAPTER_READ -> type.id.toString() + "-" + type.lastChapterRead
|
LAST_CHAPTER_READ -> type.id.toString() + "-" + type.lastChapterRead
|
||||||
@@ -290,7 +291,7 @@ class TrackQuery {
|
|||||||
data class TrackRecordCondition(
|
data class TrackRecordCondition(
|
||||||
val id: Int? = null,
|
val id: Int? = null,
|
||||||
val mangaId: Int? = null,
|
val mangaId: Int? = null,
|
||||||
val syncId: Int? = null,
|
val trackerId: Int? = null,
|
||||||
val remoteId: Long? = null,
|
val remoteId: Long? = null,
|
||||||
val libraryId: Long? = null,
|
val libraryId: Long? = null,
|
||||||
val title: String? = null,
|
val title: String? = null,
|
||||||
@@ -306,7 +307,7 @@ class TrackQuery {
|
|||||||
val opAnd = OpAnd()
|
val opAnd = OpAnd()
|
||||||
opAnd.eq(id, TrackRecordTable.id)
|
opAnd.eq(id, TrackRecordTable.id)
|
||||||
opAnd.eq(mangaId, TrackRecordTable.mangaId)
|
opAnd.eq(mangaId, TrackRecordTable.mangaId)
|
||||||
opAnd.eq(syncId, TrackRecordTable.syncId)
|
opAnd.eq(trackerId, TrackRecordTable.trackerId)
|
||||||
opAnd.eq(remoteId, TrackRecordTable.remoteId)
|
opAnd.eq(remoteId, TrackRecordTable.remoteId)
|
||||||
opAnd.eq(libraryId, TrackRecordTable.libraryId)
|
opAnd.eq(libraryId, TrackRecordTable.libraryId)
|
||||||
opAnd.eq(title, TrackRecordTable.title)
|
opAnd.eq(title, TrackRecordTable.title)
|
||||||
@@ -325,7 +326,7 @@ class TrackQuery {
|
|||||||
data class TrackRecordFilter(
|
data class TrackRecordFilter(
|
||||||
val id: IntFilter? = null,
|
val id: IntFilter? = null,
|
||||||
val mangaId: IntFilter? = null,
|
val mangaId: IntFilter? = null,
|
||||||
val syncId: IntFilter? = null,
|
val trackerId: IntFilter? = null,
|
||||||
val remoteId: LongFilter? = null,
|
val remoteId: LongFilter? = null,
|
||||||
val libraryId: LongFilter? = null,
|
val libraryId: LongFilter? = null,
|
||||||
val title: StringFilter? = null,
|
val title: StringFilter? = null,
|
||||||
@@ -344,7 +345,7 @@ class TrackQuery {
|
|||||||
return listOfNotNull(
|
return listOfNotNull(
|
||||||
andFilterWithCompareEntity(TrackRecordTable.id, id),
|
andFilterWithCompareEntity(TrackRecordTable.id, id),
|
||||||
andFilterWithCompareEntity(TrackRecordTable.mangaId, mangaId),
|
andFilterWithCompareEntity(TrackRecordTable.mangaId, mangaId),
|
||||||
andFilterWithCompare(TrackRecordTable.syncId, syncId),
|
andFilterWithCompare(TrackRecordTable.trackerId, trackerId),
|
||||||
andFilterWithCompare(TrackRecordTable.remoteId, remoteId),
|
andFilterWithCompare(TrackRecordTable.remoteId, remoteId),
|
||||||
andFilterWithCompare(TrackRecordTable.libraryId, libraryId),
|
andFilterWithCompare(TrackRecordTable.libraryId, libraryId),
|
||||||
andFilterWithCompareString(TrackRecordTable.title, title),
|
andFilterWithCompareString(TrackRecordTable.title, title),
|
||||||
@@ -462,7 +463,7 @@ class TrackQuery {
|
|||||||
"Tracker needs to be logged-in to search"
|
"Tracker needs to be logged-in to search"
|
||||||
}
|
}
|
||||||
SearchTrackerPayload(
|
SearchTrackerPayload(
|
||||||
tracker.search(input.query).map {
|
tracker.search(input.query).insertAll().map {
|
||||||
TrackSearchType(it)
|
TrackSearchType(it)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ import suwayomi.tachidesk.graphql.server.primitives.Node
|
|||||||
import suwayomi.tachidesk.graphql.server.primitives.NodeList
|
import suwayomi.tachidesk.graphql.server.primitives.NodeList
|
||||||
import suwayomi.tachidesk.graphql.server.primitives.PageInfo
|
import suwayomi.tachidesk.graphql.server.primitives.PageInfo
|
||||||
import suwayomi.tachidesk.manga.impl.track.tracker.Tracker
|
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.TrackRecordTable
|
||||||
|
import suwayomi.tachidesk.manga.model.table.TrackSearchTable
|
||||||
import java.util.concurrent.CompletableFuture
|
import java.util.concurrent.CompletableFuture
|
||||||
|
|
||||||
class TrackerType(
|
class TrackerType(
|
||||||
@@ -45,7 +45,7 @@ class TrackerType(
|
|||||||
class TrackRecordType(
|
class TrackRecordType(
|
||||||
val id: Int,
|
val id: Int,
|
||||||
val mangaId: Int,
|
val mangaId: Int,
|
||||||
val syncId: Int,
|
val trackerId: Int,
|
||||||
val remoteId: Long,
|
val remoteId: Long,
|
||||||
val libraryId: Long?,
|
val libraryId: Long?,
|
||||||
val title: String,
|
val title: String,
|
||||||
@@ -60,7 +60,7 @@ class TrackRecordType(
|
|||||||
constructor(row: ResultRow) : this(
|
constructor(row: ResultRow) : this(
|
||||||
row[TrackRecordTable.id].value,
|
row[TrackRecordTable.id].value,
|
||||||
row[TrackRecordTable.mangaId].value,
|
row[TrackRecordTable.mangaId].value,
|
||||||
row[TrackRecordTable.syncId],
|
row[TrackRecordTable.trackerId],
|
||||||
row[TrackRecordTable.remoteId],
|
row[TrackRecordTable.remoteId],
|
||||||
row[TrackRecordTable.libraryId],
|
row[TrackRecordTable.libraryId],
|
||||||
row[TrackRecordTable.title],
|
row[TrackRecordTable.title],
|
||||||
@@ -82,13 +82,14 @@ class TrackRecordType(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun tracker(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<TrackerType> {
|
fun tracker(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<TrackerType> {
|
||||||
return dataFetchingEnvironment.getValueFromDataLoader<Int, TrackerType>("TrackerDataLoader", syncId)
|
return dataFetchingEnvironment.getValueFromDataLoader<Int, TrackerType>("TrackerDataLoader", trackerId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class TrackSearchType(
|
class TrackSearchType(
|
||||||
val syncId: Int,
|
val id: Int,
|
||||||
val mediaId: Long,
|
val trackerId: Int,
|
||||||
|
val remoteId: Long,
|
||||||
val title: String,
|
val title: String,
|
||||||
val totalChapters: Int,
|
val totalChapters: Int,
|
||||||
val trackingUrl: String,
|
val trackingUrl: String,
|
||||||
@@ -98,21 +99,22 @@ class TrackSearchType(
|
|||||||
val publishingType: String,
|
val publishingType: String,
|
||||||
val startDate: String,
|
val startDate: String,
|
||||||
) {
|
) {
|
||||||
constructor(trackSearch: TrackSearch) : this(
|
constructor(row: ResultRow) : this(
|
||||||
trackSearch.sync_id,
|
row[TrackSearchTable.id].value,
|
||||||
trackSearch.media_id,
|
row[TrackSearchTable.trackerId],
|
||||||
trackSearch.title,
|
row[TrackSearchTable.remoteId],
|
||||||
trackSearch.total_chapters,
|
row[TrackSearchTable.title],
|
||||||
trackSearch.tracking_url,
|
row[TrackSearchTable.totalChapters],
|
||||||
trackSearch.cover_url,
|
row[TrackSearchTable.trackingUrl],
|
||||||
trackSearch.summary,
|
row[TrackSearchTable.coverUrl],
|
||||||
trackSearch.publishing_status,
|
row[TrackSearchTable.summary],
|
||||||
trackSearch.publishing_type,
|
row[TrackSearchTable.publishingStatus],
|
||||||
trackSearch.start_date,
|
row[TrackSearchTable.publishingType],
|
||||||
|
row[TrackSearchTable.startDate],
|
||||||
)
|
)
|
||||||
|
|
||||||
fun tracker(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<TrackerType> {
|
fun tracker(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture<TrackerType> {
|
||||||
return dataFetchingEnvironment.getValueFromDataLoader<Int, TrackerType>("TrackerDataLoader", syncId)
|
return dataFetchingEnvironment.getValueFromDataLoader<Int, TrackerType>("TrackerDataLoader", trackerId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ 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
|
||||||
import suwayomi.tachidesk.manga.impl.track.Track
|
import suwayomi.tachidesk.manga.impl.track.Track
|
||||||
import suwayomi.tachidesk.manga.model.dataclass.TrackSearchDataClass
|
|
||||||
import suwayomi.tachidesk.manga.model.dataclass.TrackerDataClass
|
import suwayomi.tachidesk.manga.model.dataclass.TrackerDataClass
|
||||||
import suwayomi.tachidesk.server.JavalinSetup.future
|
import suwayomi.tachidesk.server.JavalinSetup.future
|
||||||
import suwayomi.tachidesk.server.util.handler
|
import suwayomi.tachidesk.server.util.handler
|
||||||
@@ -104,17 +103,15 @@ object TrackController {
|
|||||||
val bind =
|
val bind =
|
||||||
handler(
|
handler(
|
||||||
queryParam<Int>("mangaId"),
|
queryParam<Int>("mangaId"),
|
||||||
|
queryParam<Int>("trackSearchId"),
|
||||||
documentWith = {
|
documentWith = {
|
||||||
withOperation {
|
withOperation {
|
||||||
summary("Track Record Bind")
|
summary("Track Record Bind")
|
||||||
description("Bind a Track Record to a Manga")
|
description("Bind a Track Record to a Manga")
|
||||||
}
|
}
|
||||||
body<TrackSearchDataClass>()
|
|
||||||
},
|
},
|
||||||
behaviorOf = { ctx, mangaId ->
|
behaviorOf = { ctx, mangaId, trackSearchId ->
|
||||||
val input = json.decodeFromString<TrackSearchDataClass>(ctx.body())
|
ctx.future(future { Track.bind(mangaId, trackSearchId) })
|
||||||
logger.debug { "tracker bind $input" }
|
|
||||||
ctx.future(future { Track.bind(mangaId, input) })
|
|
||||||
},
|
},
|
||||||
withResults = {
|
withResults = {
|
||||||
httpCode(HttpCode.OK)
|
httpCode(HttpCode.OK)
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ import suwayomi.tachidesk.manga.model.dataclass.TrackSearchDataClass
|
|||||||
import suwayomi.tachidesk.manga.model.dataclass.TrackerDataClass
|
import suwayomi.tachidesk.manga.model.dataclass.TrackerDataClass
|
||||||
import suwayomi.tachidesk.manga.model.table.ChapterTable
|
import suwayomi.tachidesk.manga.model.table.ChapterTable
|
||||||
import suwayomi.tachidesk.manga.model.table.TrackRecordTable
|
import suwayomi.tachidesk.manga.model.table.TrackRecordTable
|
||||||
|
import suwayomi.tachidesk.manga.model.table.TrackSearchTable
|
||||||
|
import suwayomi.tachidesk.manga.model.table.insertAll
|
||||||
|
|
||||||
object Track {
|
object Track {
|
||||||
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
|
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
|
||||||
@@ -66,7 +68,7 @@ object Track {
|
|||||||
transaction {
|
transaction {
|
||||||
TrackRecordTable.select { TrackRecordTable.mangaId eq mangaId }
|
TrackRecordTable.select { TrackRecordTable.mangaId eq mangaId }
|
||||||
.map { it.toTrackRecordDataClass() }
|
.map { it.toTrackRecordDataClass() }
|
||||||
}.associateBy { it.syncId }
|
}.associateBy { it.trackerId }
|
||||||
|
|
||||||
val trackers = TrackerManager.services
|
val trackers = TrackerManager.services
|
||||||
return trackers
|
return trackers
|
||||||
@@ -95,29 +97,42 @@ object Track {
|
|||||||
suspend fun search(input: SearchInput): List<TrackSearchDataClass> {
|
suspend fun search(input: SearchInput): List<TrackSearchDataClass> {
|
||||||
val tracker = TrackerManager.getTracker(input.trackerId)!!
|
val tracker = TrackerManager.getTracker(input.trackerId)!!
|
||||||
val list = tracker.search(input.title)
|
val list = tracker.search(input.title)
|
||||||
return list.map {
|
return list.insertAll().map {
|
||||||
TrackSearchDataClass(
|
TrackSearchDataClass(
|
||||||
syncId = it.sync_id,
|
id = it[TrackSearchTable.id].value,
|
||||||
mediaId = it.media_id,
|
trackerId = it[TrackSearchTable.trackerId],
|
||||||
title = it.title,
|
remoteId = it[TrackSearchTable.remoteId],
|
||||||
totalChapters = it.total_chapters,
|
title = it[TrackSearchTable.title],
|
||||||
trackingUrl = it.tracking_url,
|
totalChapters = it[TrackSearchTable.totalChapters],
|
||||||
coverUrl = it.cover_url,
|
trackingUrl = it[TrackSearchTable.trackingUrl],
|
||||||
summary = it.summary,
|
coverUrl = it[TrackSearchTable.coverUrl],
|
||||||
publishingStatus = it.publishing_status,
|
summary = it[TrackSearchTable.summary],
|
||||||
publishingType = it.publishing_type,
|
publishingStatus = it[TrackSearchTable.publishingStatus],
|
||||||
startDate = it.start_date,
|
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(
|
suspend fun bind(
|
||||||
mangaId: Int,
|
mangaId: Int,
|
||||||
input: TrackSearchDataClass,
|
trackSearchId: Int,
|
||||||
) {
|
) {
|
||||||
val tracker = TrackerManager.getTracker(input.syncId)!!
|
val track =
|
||||||
|
transaction {
|
||||||
val track = input.toTrack(mangaId)
|
TrackSearchTable.select { TrackSearchTable.id eq trackSearchId }.first()
|
||||||
|
.toTrack(mangaId)
|
||||||
|
}
|
||||||
|
val tracker = TrackerManager.getTracker(track.sync_id)!!
|
||||||
|
|
||||||
val chapter = queryMaxReadChapter(mangaId)
|
val chapter = queryMaxReadChapter(mangaId)
|
||||||
val hasReadChapters = chapter != null
|
val hasReadChapters = chapter != null
|
||||||
@@ -168,7 +183,7 @@ object Track {
|
|||||||
TrackRecordTable.select { TrackRecordTable.id eq input.recordId }.first()
|
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) {
|
if (input.status != null) {
|
||||||
recordDb[TrackRecordTable.status] = input.status
|
recordDb[TrackRecordTable.status] = input.status
|
||||||
@@ -250,7 +265,7 @@ object Track {
|
|||||||
}
|
}
|
||||||
|
|
||||||
records.forEach {
|
records.forEach {
|
||||||
val tracker = TrackerManager.getTracker(it[TrackRecordTable.syncId])
|
val tracker = TrackerManager.getTracker(it[TrackRecordTable.trackerId])
|
||||||
val lastChapterRead = it[TrackRecordTable.lastChapterRead]
|
val lastChapterRead = it[TrackRecordTable.lastChapterRead]
|
||||||
val isLogin = tracker?.isLoggedIn == true
|
val isLogin = tracker?.isLoggedIn == true
|
||||||
logger.debug {
|
logger.debug {
|
||||||
@@ -271,14 +286,14 @@ object Track {
|
|||||||
val existingRecord =
|
val existingRecord =
|
||||||
TrackRecordTable.select {
|
TrackRecordTable.select {
|
||||||
(TrackRecordTable.mangaId eq track.manga_id) and
|
(TrackRecordTable.mangaId eq track.manga_id) and
|
||||||
(TrackRecordTable.syncId eq track.sync_id)
|
(TrackRecordTable.trackerId eq track.sync_id)
|
||||||
}
|
}
|
||||||
.singleOrNull()
|
.singleOrNull()
|
||||||
|
|
||||||
if (existingRecord != null) {
|
if (existingRecord != null) {
|
||||||
TrackRecordTable.update({
|
TrackRecordTable.update({
|
||||||
(TrackRecordTable.mangaId eq track.manga_id) and
|
(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[remoteId] = track.media_id
|
||||||
it[libraryId] = track.library_id
|
it[libraryId] = track.library_id
|
||||||
@@ -295,7 +310,7 @@ object Track {
|
|||||||
} else {
|
} else {
|
||||||
TrackRecordTable.insertAndGetId {
|
TrackRecordTable.insertAndGetId {
|
||||||
it[mangaId] = track.manga_id
|
it[mangaId] = track.manga_id
|
||||||
it[syncId] = track.sync_id
|
it[trackerId] = track.sync_id
|
||||||
it[remoteId] = track.media_id
|
it[remoteId] = track.media_id
|
||||||
it[libraryId] = track.library_id
|
it[libraryId] = track.library_id
|
||||||
it[title] = track.title
|
it[title] = track.title
|
||||||
|
|||||||
@@ -58,12 +58,12 @@ class TrackerPreferences {
|
|||||||
fun autoUpdateTrack() = preferenceStore.getBoolean("pref_auto_update_manga_sync_key", true)
|
fun autoUpdateTrack() = preferenceStore.getBoolean("pref_auto_update_manga_sync_key", true)
|
||||||
|
|
||||||
companion object {
|
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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,23 +2,13 @@ package suwayomi.tachidesk.manga.impl.track.tracker.model
|
|||||||
|
|
||||||
import org.jetbrains.exposed.sql.ResultRow
|
import org.jetbrains.exposed.sql.ResultRow
|
||||||
import suwayomi.tachidesk.manga.model.dataclass.TrackRecordDataClass
|
import suwayomi.tachidesk.manga.model.dataclass.TrackRecordDataClass
|
||||||
import suwayomi.tachidesk.manga.model.dataclass.TrackSearchDataClass
|
|
||||||
import suwayomi.tachidesk.manga.model.table.TrackRecordTable
|
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 =
|
fun ResultRow.toTrackRecordDataClass(): TrackRecordDataClass =
|
||||||
TrackRecordDataClass(
|
TrackRecordDataClass(
|
||||||
id = this[TrackRecordTable.id].value,
|
id = this[TrackRecordTable.id].value,
|
||||||
mangaId = this[TrackRecordTable.mangaId].value,
|
mangaId = this[TrackRecordTable.mangaId].value,
|
||||||
syncId = this[TrackRecordTable.syncId],
|
trackerId = this[TrackRecordTable.trackerId],
|
||||||
remoteId = this[TrackRecordTable.remoteId],
|
remoteId = this[TrackRecordTable.remoteId],
|
||||||
libraryId = this[TrackRecordTable.libraryId],
|
libraryId = this[TrackRecordTable.libraryId],
|
||||||
title = this[TrackRecordTable.title],
|
title = this[TrackRecordTable.title],
|
||||||
@@ -32,7 +22,7 @@ fun ResultRow.toTrackRecordDataClass(): TrackRecordDataClass =
|
|||||||
)
|
)
|
||||||
|
|
||||||
fun ResultRow.toTrack(): Track =
|
fun ResultRow.toTrack(): Track =
|
||||||
Track.create(this[TrackRecordTable.syncId]).also {
|
Track.create(this[TrackRecordTable.trackerId]).also {
|
||||||
it.id = this[TrackRecordTable.id].value
|
it.id = this[TrackRecordTable.id].value
|
||||||
it.manga_id = this[TrackRecordTable.mangaId].value
|
it.manga_id = this[TrackRecordTable.mangaId].value
|
||||||
it.media_id = this[TrackRecordTable.remoteId]
|
it.media_id = this[TrackRecordTable.remoteId]
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ package suwayomi.tachidesk.manga.model.dataclass
|
|||||||
data class TrackRecordDataClass(
|
data class TrackRecordDataClass(
|
||||||
val id: Int,
|
val id: Int,
|
||||||
val mangaId: Int,
|
val mangaId: Int,
|
||||||
val syncId: Int,
|
val trackerId: Int,
|
||||||
val remoteId: Long,
|
val remoteId: Long,
|
||||||
val libraryId: Long?,
|
val libraryId: Long?,
|
||||||
val title: String,
|
val title: String,
|
||||||
|
|||||||
@@ -11,8 +11,9 @@ import kotlinx.serialization.Serializable
|
|||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class TrackSearchDataClass(
|
data class TrackSearchDataClass(
|
||||||
val syncId: Int,
|
val id: Int,
|
||||||
val mediaId: Long,
|
val trackerId: Int,
|
||||||
|
val remoteId: Long,
|
||||||
val title: String,
|
val title: String,
|
||||||
val totalChapters: Int,
|
val totalChapters: Int,
|
||||||
val trackingUrl: String,
|
val trackingUrl: String,
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import org.jetbrains.exposed.sql.ReferenceOption
|
|||||||
|
|
||||||
object TrackRecordTable : IntIdTable() {
|
object TrackRecordTable : IntIdTable() {
|
||||||
val mangaId = reference("manga_id", MangaTable, ReferenceOption.CASCADE)
|
val mangaId = reference("manga_id", MangaTable, ReferenceOption.CASCADE)
|
||||||
val syncId = integer("sync_id")
|
val trackerId = integer("sync_id")
|
||||||
val remoteId = long("remote_id")
|
val remoteId = long("remote_id")
|
||||||
val libraryId = long("library_id").nullable()
|
val libraryId = long("library_id").nullable()
|
||||||
val title = varchar("title", 512)
|
val title = varchar("title", 512)
|
||||||
|
|||||||
@@ -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<TrackSearch>.insertAll(): List<ResultRow> {
|
||||||
|
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<Boolean, MutableList<Pair<Int?, TrackSearch>>>()
|
||||||
|
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]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<Table>
|
||||||
|
get() =
|
||||||
|
arrayOf(
|
||||||
|
TrackSearchTable(),
|
||||||
|
)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user