mirror of
https://github.com/Suwayomi/Suwayomi-Server.git
synced 2026-07-04 03:14:40 -05:00
Downloader Queries and Mutations (#610)
* Add downloader GraphQL endpoints * Fix names * DeleteDownloadedChapter(s) * DequeueChapterDownload(s)
This commit is contained in:
@@ -0,0 +1,260 @@
|
|||||||
|
package suwayomi.tachidesk.graphql.mutations
|
||||||
|
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
|
import kotlinx.coroutines.withTimeout
|
||||||
|
import org.jetbrains.exposed.sql.select
|
||||||
|
import org.jetbrains.exposed.sql.transactions.transaction
|
||||||
|
import suwayomi.tachidesk.graphql.types.ChapterType
|
||||||
|
import suwayomi.tachidesk.graphql.types.DownloadStatus
|
||||||
|
import suwayomi.tachidesk.manga.impl.Chapter
|
||||||
|
import suwayomi.tachidesk.manga.impl.download.DownloadManager
|
||||||
|
import suwayomi.tachidesk.manga.impl.download.model.Status
|
||||||
|
import suwayomi.tachidesk.manga.model.table.ChapterTable
|
||||||
|
import suwayomi.tachidesk.server.JavalinSetup.future
|
||||||
|
import java.util.concurrent.CompletableFuture
|
||||||
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
|
class DownloadMutation {
|
||||||
|
|
||||||
|
data class DeleteDownloadedChaptersInput(
|
||||||
|
val clientMutationId: String? = null,
|
||||||
|
val ids: List<Int>
|
||||||
|
)
|
||||||
|
data class DeleteDownloadedChaptersPayload(
|
||||||
|
val clientMutationId: String?,
|
||||||
|
val chapters: List<ChapterType>
|
||||||
|
)
|
||||||
|
|
||||||
|
fun deleteDownloadedChapters(input: DeleteDownloadedChaptersInput): DeleteDownloadedChaptersPayload {
|
||||||
|
val (clientMutationId, chapters) = input
|
||||||
|
|
||||||
|
Chapter.deleteChapters(chapters)
|
||||||
|
|
||||||
|
return DeleteDownloadedChaptersPayload(
|
||||||
|
clientMutationId = clientMutationId,
|
||||||
|
chapters = transaction {
|
||||||
|
ChapterTable.select { ChapterTable.id inList chapters }
|
||||||
|
.map { ChapterType(it) }
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
data class DeleteDownloadedChapterInput(
|
||||||
|
val clientMutationId: String? = null,
|
||||||
|
val id: Int
|
||||||
|
)
|
||||||
|
data class DeleteDownloadedChapterPayload(
|
||||||
|
val clientMutationId: String?,
|
||||||
|
val chapters: ChapterType
|
||||||
|
)
|
||||||
|
|
||||||
|
fun deleteDownloadedChapter(input: DeleteDownloadedChapterInput): DeleteDownloadedChapterPayload {
|
||||||
|
val (clientMutationId, chapter) = input
|
||||||
|
|
||||||
|
Chapter.deleteChapters(listOf(chapter))
|
||||||
|
|
||||||
|
return DeleteDownloadedChapterPayload(
|
||||||
|
clientMutationId = clientMutationId,
|
||||||
|
chapters = transaction {
|
||||||
|
ChapterType(ChapterTable.select { ChapterTable.id eq chapter }.first())
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
data class EnqueueChapterDownloadsInput(
|
||||||
|
val clientMutationId: String? = null,
|
||||||
|
val ids: List<Int>
|
||||||
|
)
|
||||||
|
data class EnqueueChapterDownloadsPayload(
|
||||||
|
val clientMutationId: String?,
|
||||||
|
val downloadStatus: DownloadStatus
|
||||||
|
)
|
||||||
|
|
||||||
|
fun enqueueChapterDownloads(
|
||||||
|
input: EnqueueChapterDownloadsInput
|
||||||
|
): CompletableFuture<EnqueueChapterDownloadsPayload> {
|
||||||
|
val (clientMutationId, chapters) = input
|
||||||
|
|
||||||
|
DownloadManager.enqueue(DownloadManager.EnqueueInput(chapters))
|
||||||
|
|
||||||
|
return future {
|
||||||
|
EnqueueChapterDownloadsPayload(
|
||||||
|
clientMutationId = clientMutationId,
|
||||||
|
downloadStatus = withTimeout(30.seconds) {
|
||||||
|
DownloadStatus(DownloadManager.status.first { it.queue.any { it.chapter.id in chapters } })
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class EnqueueChapterDownloadInput(
|
||||||
|
val clientMutationId: String? = null,
|
||||||
|
val id: Int
|
||||||
|
)
|
||||||
|
data class EnqueueChapterDownloadPayload(
|
||||||
|
val clientMutationId: String?,
|
||||||
|
val downloadStatus: DownloadStatus
|
||||||
|
)
|
||||||
|
|
||||||
|
fun enqueueChapterDownload(
|
||||||
|
input: EnqueueChapterDownloadInput
|
||||||
|
): CompletableFuture<EnqueueChapterDownloadPayload> {
|
||||||
|
val (clientMutationId, chapter) = input
|
||||||
|
|
||||||
|
DownloadManager.enqueue(DownloadManager.EnqueueInput(listOf(chapter)))
|
||||||
|
|
||||||
|
return future {
|
||||||
|
EnqueueChapterDownloadPayload(
|
||||||
|
clientMutationId = clientMutationId,
|
||||||
|
downloadStatus = withTimeout(30.seconds) {
|
||||||
|
DownloadStatus(DownloadManager.status.first { it.queue.any { it.chapter.id == chapter } })
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class DequeueChapterDownloadsInput(
|
||||||
|
val clientMutationId: String? = null,
|
||||||
|
val ids: List<Int>
|
||||||
|
)
|
||||||
|
data class DequeueChapterDownloadsPayload(
|
||||||
|
val clientMutationId: String?,
|
||||||
|
val downloadStatus: DownloadStatus
|
||||||
|
)
|
||||||
|
|
||||||
|
fun dequeueChapterDownloads(
|
||||||
|
input: DequeueChapterDownloadsInput
|
||||||
|
): CompletableFuture<DequeueChapterDownloadsPayload> {
|
||||||
|
val (clientMutationId, chapters) = input
|
||||||
|
|
||||||
|
DownloadManager.dequeue(DownloadManager.EnqueueInput(chapters))
|
||||||
|
|
||||||
|
return future {
|
||||||
|
DequeueChapterDownloadsPayload(
|
||||||
|
clientMutationId = clientMutationId,
|
||||||
|
downloadStatus = withTimeout(30.seconds) {
|
||||||
|
DownloadStatus(DownloadManager.status.first { it.queue.none { it.chapter.id in chapters } })
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class DequeueChapterDownloadInput(
|
||||||
|
val clientMutationId: String? = null,
|
||||||
|
val id: Int
|
||||||
|
)
|
||||||
|
data class DequeueChapterDownloadPayload(
|
||||||
|
val clientMutationId: String?,
|
||||||
|
val downloadStatus: DownloadStatus
|
||||||
|
)
|
||||||
|
|
||||||
|
fun dequeueChapterDownload(
|
||||||
|
input: DequeueChapterDownloadInput
|
||||||
|
): CompletableFuture<DequeueChapterDownloadPayload> {
|
||||||
|
val (clientMutationId, chapter) = input
|
||||||
|
|
||||||
|
DownloadManager.dequeue(DownloadManager.EnqueueInput(listOf(chapter)))
|
||||||
|
|
||||||
|
return future {
|
||||||
|
DequeueChapterDownloadPayload(
|
||||||
|
clientMutationId = clientMutationId,
|
||||||
|
downloadStatus = withTimeout(30.seconds) {
|
||||||
|
DownloadStatus(DownloadManager.status.first { it.queue.none { it.chapter.id == chapter } })
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class StartDownloaderInput(
|
||||||
|
val clientMutationId: String? = null
|
||||||
|
)
|
||||||
|
data class StartDownloaderPayload(
|
||||||
|
val clientMutationId: String?,
|
||||||
|
val downloadStatus: DownloadStatus
|
||||||
|
)
|
||||||
|
|
||||||
|
fun startDownloader(input: StartDownloaderInput): CompletableFuture<StartDownloaderPayload> {
|
||||||
|
DownloadManager.start()
|
||||||
|
|
||||||
|
return future {
|
||||||
|
StartDownloaderPayload(
|
||||||
|
input.clientMutationId,
|
||||||
|
downloadStatus = withTimeout(30.seconds) {
|
||||||
|
DownloadStatus(
|
||||||
|
DownloadManager.status.first { it.status == Status.Started }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class StopDownloaderInput(
|
||||||
|
val clientMutationId: String? = null
|
||||||
|
)
|
||||||
|
data class StopDownloaderPayload(
|
||||||
|
val clientMutationId: String?,
|
||||||
|
val downloadStatus: DownloadStatus
|
||||||
|
)
|
||||||
|
|
||||||
|
fun stopDownloader(input: StopDownloaderInput): CompletableFuture<StopDownloaderPayload> {
|
||||||
|
return future {
|
||||||
|
DownloadManager.stop()
|
||||||
|
StopDownloaderPayload(
|
||||||
|
input.clientMutationId,
|
||||||
|
downloadStatus = withTimeout(30.seconds) {
|
||||||
|
DownloadStatus(
|
||||||
|
DownloadManager.status.first { it.status == Status.Stopped }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class ClearDownloaderInput(
|
||||||
|
val clientMutationId: String? = null
|
||||||
|
)
|
||||||
|
data class ClearDownloaderPayload(
|
||||||
|
val clientMutationId: String?,
|
||||||
|
val downloadStatus: DownloadStatus
|
||||||
|
)
|
||||||
|
|
||||||
|
fun clearDownloader(input: ClearDownloaderInput): CompletableFuture<ClearDownloaderPayload> {
|
||||||
|
return future {
|
||||||
|
DownloadManager.clear()
|
||||||
|
ClearDownloaderPayload(
|
||||||
|
input.clientMutationId,
|
||||||
|
downloadStatus = withTimeout(30.seconds) {
|
||||||
|
DownloadStatus(
|
||||||
|
DownloadManager.status.first { it.status == Status.Stopped && it.queue.isEmpty() }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class ReorderChapterDownloadInput(
|
||||||
|
val clientMutationId: String? = null,
|
||||||
|
val chapterId: Int,
|
||||||
|
val to: Int
|
||||||
|
)
|
||||||
|
data class ReorderChapterDownloadPayload(
|
||||||
|
val clientMutationId: String?,
|
||||||
|
val downloadStatus: DownloadStatus
|
||||||
|
)
|
||||||
|
|
||||||
|
fun reorderChapterDownload(input: ReorderChapterDownloadInput): CompletableFuture<ReorderChapterDownloadPayload> {
|
||||||
|
val (clientMutationId, chapter, to) = input
|
||||||
|
DownloadManager.reorder(chapter, to)
|
||||||
|
|
||||||
|
return future {
|
||||||
|
ReorderChapterDownloadPayload(
|
||||||
|
clientMutationId,
|
||||||
|
downloadStatus = withTimeout(30.seconds) {
|
||||||
|
DownloadStatus(
|
||||||
|
DownloadManager.status.first { it.queue.indexOfFirst { it.chapter.id == chapter } <= to }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package suwayomi.tachidesk.graphql.queries
|
||||||
|
|
||||||
|
import suwayomi.tachidesk.graphql.types.DownloadStatus
|
||||||
|
import suwayomi.tachidesk.manga.impl.download.DownloadManager
|
||||||
|
|
||||||
|
class DownloadQuery {
|
||||||
|
|
||||||
|
fun downloadStatus(): DownloadStatus {
|
||||||
|
return DownloadStatus(DownloadManager.status.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,6 +16,7 @@ import io.javalin.http.UploadedFile
|
|||||||
import suwayomi.tachidesk.graphql.mutations.BackupMutation
|
import suwayomi.tachidesk.graphql.mutations.BackupMutation
|
||||||
import suwayomi.tachidesk.graphql.mutations.CategoryMutation
|
import suwayomi.tachidesk.graphql.mutations.CategoryMutation
|
||||||
import suwayomi.tachidesk.graphql.mutations.ChapterMutation
|
import suwayomi.tachidesk.graphql.mutations.ChapterMutation
|
||||||
|
import suwayomi.tachidesk.graphql.mutations.DownloadMutation
|
||||||
import suwayomi.tachidesk.graphql.mutations.ExtensionMutation
|
import suwayomi.tachidesk.graphql.mutations.ExtensionMutation
|
||||||
import suwayomi.tachidesk.graphql.mutations.MangaMutation
|
import suwayomi.tachidesk.graphql.mutations.MangaMutation
|
||||||
import suwayomi.tachidesk.graphql.mutations.MetaMutation
|
import suwayomi.tachidesk.graphql.mutations.MetaMutation
|
||||||
@@ -24,6 +25,7 @@ import suwayomi.tachidesk.graphql.mutations.UpdateMutation
|
|||||||
import suwayomi.tachidesk.graphql.queries.BackupQuery
|
import suwayomi.tachidesk.graphql.queries.BackupQuery
|
||||||
import suwayomi.tachidesk.graphql.queries.CategoryQuery
|
import suwayomi.tachidesk.graphql.queries.CategoryQuery
|
||||||
import suwayomi.tachidesk.graphql.queries.ChapterQuery
|
import suwayomi.tachidesk.graphql.queries.ChapterQuery
|
||||||
|
import suwayomi.tachidesk.graphql.queries.DownloadQuery
|
||||||
import suwayomi.tachidesk.graphql.queries.ExtensionQuery
|
import suwayomi.tachidesk.graphql.queries.ExtensionQuery
|
||||||
import suwayomi.tachidesk.graphql.queries.MangaQuery
|
import suwayomi.tachidesk.graphql.queries.MangaQuery
|
||||||
import suwayomi.tachidesk.graphql.queries.MetaQuery
|
import suwayomi.tachidesk.graphql.queries.MetaQuery
|
||||||
@@ -57,6 +59,7 @@ val schema = toSchema(
|
|||||||
TopLevelObject(BackupQuery()),
|
TopLevelObject(BackupQuery()),
|
||||||
TopLevelObject(CategoryQuery()),
|
TopLevelObject(CategoryQuery()),
|
||||||
TopLevelObject(ChapterQuery()),
|
TopLevelObject(ChapterQuery()),
|
||||||
|
TopLevelObject(DownloadQuery()),
|
||||||
TopLevelObject(ExtensionQuery()),
|
TopLevelObject(ExtensionQuery()),
|
||||||
TopLevelObject(MangaQuery()),
|
TopLevelObject(MangaQuery()),
|
||||||
TopLevelObject(MetaQuery()),
|
TopLevelObject(MetaQuery()),
|
||||||
@@ -67,6 +70,7 @@ val schema = toSchema(
|
|||||||
TopLevelObject(BackupMutation()),
|
TopLevelObject(BackupMutation()),
|
||||||
TopLevelObject(CategoryMutation()),
|
TopLevelObject(CategoryMutation()),
|
||||||
TopLevelObject(ChapterMutation()),
|
TopLevelObject(ChapterMutation()),
|
||||||
|
TopLevelObject(DownloadMutation()),
|
||||||
TopLevelObject(ExtensionMutation()),
|
TopLevelObject(ExtensionMutation()),
|
||||||
TopLevelObject(MangaMutation()),
|
TopLevelObject(MangaMutation()),
|
||||||
TopLevelObject(MetaMutation()),
|
TopLevelObject(MetaMutation()),
|
||||||
|
|||||||
@@ -7,19 +7,16 @@
|
|||||||
|
|
||||||
package suwayomi.tachidesk.graphql.subscriptions
|
package suwayomi.tachidesk.graphql.subscriptions
|
||||||
|
|
||||||
import graphql.schema.DataFetchingEnvironment
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import suwayomi.tachidesk.graphql.server.subscriptions.FlowSubscriptionSource
|
import suwayomi.tachidesk.graphql.types.DownloadStatus
|
||||||
import suwayomi.tachidesk.graphql.types.DownloadType
|
import suwayomi.tachidesk.manga.impl.download.DownloadManager
|
||||||
import suwayomi.tachidesk.manga.impl.download.model.DownloadChapter
|
|
||||||
|
|
||||||
val downloadSubscriptionSource = FlowSubscriptionSource<DownloadChapter>()
|
|
||||||
|
|
||||||
class DownloadSubscription {
|
class DownloadSubscription {
|
||||||
fun downloadChanged(dataFetchingEnvironment: DataFetchingEnvironment): Flow<DownloadType> {
|
|
||||||
return downloadSubscriptionSource.emitter.map { downloadChapter ->
|
fun downloadChanged(): Flow<DownloadStatus> {
|
||||||
DownloadType(downloadChapter)
|
return DownloadManager.status.map { downloadStatus ->
|
||||||
|
DownloadStatus(downloadStatus)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
package suwayomi.tachidesk.graphql.types
|
package suwayomi.tachidesk.graphql.types
|
||||||
|
|
||||||
|
import com.expediagroup.graphql.generator.annotations.GraphQLIgnore
|
||||||
import com.expediagroup.graphql.server.extensions.getValueFromDataLoader
|
import com.expediagroup.graphql.server.extensions.getValueFromDataLoader
|
||||||
import graphql.schema.DataFetchingEnvironment
|
import graphql.schema.DataFetchingEnvironment
|
||||||
import suwayomi.tachidesk.graphql.server.primitives.Cursor
|
import suwayomi.tachidesk.graphql.server.primitives.Cursor
|
||||||
@@ -15,20 +16,42 @@ 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.download.model.DownloadChapter
|
import suwayomi.tachidesk.manga.impl.download.model.DownloadChapter
|
||||||
import suwayomi.tachidesk.manga.impl.download.model.DownloadState
|
import suwayomi.tachidesk.manga.impl.download.model.DownloadStatus
|
||||||
|
import suwayomi.tachidesk.manga.impl.download.model.Status
|
||||||
import java.util.concurrent.CompletableFuture
|
import java.util.concurrent.CompletableFuture
|
||||||
|
import suwayomi.tachidesk.manga.impl.download.model.DownloadState as OtherDownloadState
|
||||||
|
|
||||||
|
data class DownloadStatus(
|
||||||
|
val state: DownloaderState,
|
||||||
|
val queue: List<DownloadType>
|
||||||
|
) {
|
||||||
|
constructor(downloadStatus: DownloadStatus) : this(
|
||||||
|
when (downloadStatus.status) {
|
||||||
|
Status.Stopped -> DownloaderState.STOPPED
|
||||||
|
Status.Started -> DownloaderState.STARTED
|
||||||
|
},
|
||||||
|
downloadStatus.queue.map { DownloadType(it) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
class DownloadType(
|
class DownloadType(
|
||||||
|
@get:GraphQLIgnore
|
||||||
val chapterId: Int,
|
val chapterId: Int,
|
||||||
|
@get:GraphQLIgnore
|
||||||
val mangaId: Int,
|
val mangaId: Int,
|
||||||
var state: DownloadState = DownloadState.Queued,
|
val state: DownloadState,
|
||||||
var progress: Float = 0f,
|
val progress: Float,
|
||||||
var tries: Int = 0
|
val tries: Int
|
||||||
) : Node {
|
) : Node {
|
||||||
constructor(downloadChapter: DownloadChapter) : this(
|
constructor(downloadChapter: DownloadChapter) : this(
|
||||||
downloadChapter.chapter.id,
|
downloadChapter.chapter.id,
|
||||||
downloadChapter.mangaId,
|
downloadChapter.mangaId,
|
||||||
downloadChapter.state,
|
when (downloadChapter.state) {
|
||||||
|
OtherDownloadState.Queued -> DownloadState.QUEUED
|
||||||
|
OtherDownloadState.Downloading -> DownloadState.DOWNLOADING
|
||||||
|
OtherDownloadState.Finished -> DownloadState.FINISHED
|
||||||
|
OtherDownloadState.Error -> DownloadState.ERROR
|
||||||
|
},
|
||||||
downloadChapter.progress,
|
downloadChapter.progress,
|
||||||
downloadChapter.tries
|
downloadChapter.tries
|
||||||
)
|
)
|
||||||
@@ -42,6 +65,18 @@ class DownloadType(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class DownloadState {
|
||||||
|
QUEUED,
|
||||||
|
DOWNLOADING,
|
||||||
|
FINISHED,
|
||||||
|
ERROR
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class DownloaderState {
|
||||||
|
STARTED,
|
||||||
|
STOPPED
|
||||||
|
}
|
||||||
|
|
||||||
data class DownloadNodeList(
|
data class DownloadNodeList(
|
||||||
override val nodes: List<DownloadType>,
|
override val nodes: List<DownloadType>,
|
||||||
override val edges: List<DownloadEdge>,
|
override val edges: List<DownloadEdge>,
|
||||||
|
|||||||
@@ -393,21 +393,7 @@ object Chapter {
|
|||||||
|
|
||||||
private fun deleteChapters(input: MangaChapterBatchEditInput, mangaId: Int? = null) {
|
private fun deleteChapters(input: MangaChapterBatchEditInput, mangaId: Int? = null) {
|
||||||
if (input.chapterIds != null) {
|
if (input.chapterIds != null) {
|
||||||
val chapterIds = input.chapterIds
|
deleteChapters(input.chapterIds)
|
||||||
|
|
||||||
transaction {
|
|
||||||
ChapterTable.slice(ChapterTable.manga, ChapterTable.id)
|
|
||||||
.select { ChapterTable.id inList chapterIds }
|
|
||||||
.forEach { row ->
|
|
||||||
val chapterMangaId = row[ChapterTable.manga].value
|
|
||||||
val chapterId = row[ChapterTable.id].value
|
|
||||||
ChapterDownloadHelper.delete(chapterMangaId, chapterId)
|
|
||||||
}
|
|
||||||
|
|
||||||
ChapterTable.update({ ChapterTable.id inList chapterIds }) {
|
|
||||||
it[isDownloaded] = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (input.chapterIndexes != null && mangaId != null) {
|
} else if (input.chapterIndexes != null && mangaId != null) {
|
||||||
transaction {
|
transaction {
|
||||||
val chapterIds = ChapterTable.slice(ChapterTable.manga, ChapterTable.id)
|
val chapterIds = ChapterTable.slice(ChapterTable.manga, ChapterTable.id)
|
||||||
@@ -426,6 +412,22 @@ object Chapter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun deleteChapters(chapterIds: List<Int>) {
|
||||||
|
transaction {
|
||||||
|
ChapterTable.slice(ChapterTable.manga, ChapterTable.id)
|
||||||
|
.select { ChapterTable.id inList chapterIds }
|
||||||
|
.forEach { row ->
|
||||||
|
val chapterMangaId = row[ChapterTable.manga].value
|
||||||
|
val chapterId = row[ChapterTable.id].value
|
||||||
|
ChapterDownloadHelper.delete(chapterMangaId, chapterId)
|
||||||
|
}
|
||||||
|
|
||||||
|
ChapterTable.update({ ChapterTable.id inList chapterIds }) {
|
||||||
|
it[isDownloaded] = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun getRecentChapters(pageNum: Int): PaginatedList<MangaChapterDataClass> {
|
fun getRecentChapters(pageNum: Int): PaginatedList<MangaChapterDataClass> {
|
||||||
return paginatedFrom(pageNum) {
|
return paginatedFrom(pageNum) {
|
||||||
transaction {
|
transaction {
|
||||||
|
|||||||
@@ -19,14 +19,16 @@ import kotlinx.coroutines.awaitAll
|
|||||||
import kotlinx.coroutines.channels.BufferOverflow
|
import kotlinx.coroutines.channels.BufferOverflow
|
||||||
import kotlinx.coroutines.coroutineScope
|
import kotlinx.coroutines.coroutineScope
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.sample
|
import kotlinx.coroutines.flow.sample
|
||||||
|
import kotlinx.coroutines.flow.stateIn
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import mu.KotlinLogging
|
import mu.KotlinLogging
|
||||||
import org.jetbrains.exposed.sql.and
|
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.subscriptions.downloadSubscriptionSource
|
|
||||||
import suwayomi.tachidesk.manga.impl.download.model.DownloadChapter
|
import suwayomi.tachidesk.manga.impl.download.model.DownloadChapter
|
||||||
import suwayomi.tachidesk.manga.impl.download.model.DownloadState.Downloading
|
import suwayomi.tachidesk.manga.impl.download.model.DownloadState.Downloading
|
||||||
import suwayomi.tachidesk.manga.impl.download.model.DownloadState.Error
|
import suwayomi.tachidesk.manga.impl.download.model.DownloadState.Error
|
||||||
@@ -108,6 +110,12 @@ object DownloadManager {
|
|||||||
|
|
||||||
private val notifyFlow = MutableSharedFlow<Unit>(extraBufferCapacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
|
private val notifyFlow = MutableSharedFlow<Unit>(extraBufferCapacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
|
||||||
|
|
||||||
|
val status = notifyFlow.sample(1.seconds)
|
||||||
|
.map {
|
||||||
|
getStatus()
|
||||||
|
}
|
||||||
|
.stateIn(scope, SharingStarted.Eagerly, getStatus())
|
||||||
|
|
||||||
init {
|
init {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
notifyFlow.sample(1.seconds).collect {
|
notifyFlow.sample(1.seconds).collect {
|
||||||
@@ -268,7 +276,6 @@ object DownloadManager {
|
|||||||
)
|
)
|
||||||
downloadQueue.add(newDownloadChapter)
|
downloadQueue.add(newDownloadChapter)
|
||||||
saveDownloadQueue()
|
saveDownloadQueue()
|
||||||
downloadSubscriptionSource.publish(newDownloadChapter)
|
|
||||||
logger.debug { "Added chapter ${chapter.id} to download queue ($newDownloadChapter)" }
|
logger.debug { "Added chapter ${chapter.id} to download queue ($newDownloadChapter)" }
|
||||||
return newDownloadChapter
|
return newDownloadChapter
|
||||||
}
|
}
|
||||||
@@ -317,6 +324,15 @@ object DownloadManager {
|
|||||||
saveDownloadQueue()
|
saveDownloadQueue()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun reorder(chapterId: Int, to: Int) {
|
||||||
|
require(to >= 0) { "'to' must be over or equal to 0" }
|
||||||
|
val download = downloadQueue.find { it.chapter.id == chapterId }
|
||||||
|
?: return
|
||||||
|
downloadQueue -= download
|
||||||
|
downloadQueue.add(to, download)
|
||||||
|
saveDownloadQueue()
|
||||||
|
}
|
||||||
|
|
||||||
fun start() {
|
fun start() {
|
||||||
logger.debug { "start" }
|
logger.debug { "start" }
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user