Skip to content

Commit

Permalink
Feature/automatically download new chapters (#596)
Browse files Browse the repository at this point in the history
* Automatically download new chapters

* Log queued downloads

* Add function to get number of manga chapters
  • Loading branch information
schroda authored Jul 20, 2023
1 parent c1d702a commit 8690e91
Show file tree
Hide file tree
Showing 6 changed files with 59 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -122,17 +122,21 @@ class ChapterMutation {
val (clientMutationId, mangaId) = input

return future {
val numberOfCurrentChapters = Chapter.getCountOfMangaChapters(mangaId)
Chapter.fetchChapterList(mangaId)
}.thenApply {
numberOfCurrentChapters
}.thenApply { numberOfCurrentChapters ->
val chapters = transaction {
ChapterTable.select { ChapterTable.manga eq mangaId }
.orderBy(ChapterTable.sourceOrder)
.map { ChapterType(it) }
}

// download new chapters if settings flag is enabled
Chapter.downloadNewChapters(mangaId, numberOfCurrentChapters, chapters.toList())

FetchChaptersPayload(
clientMutationId = clientMutationId,
chapters = chapters
chapters = chapters.map { ChapterType(it) }
)
}
}
Expand Down
49 changes: 48 additions & 1 deletion server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Chapter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.util.chapter.ChapterRecognition
import kotlinx.serialization.Serializable
import mu.KotlinLogging
import org.jetbrains.exposed.dao.id.EntityID
import org.jetbrains.exposed.sql.Op
import org.jetbrains.exposed.sql.ResultRow
import org.jetbrains.exposed.sql.SortOrder
import org.jetbrains.exposed.sql.SortOrder.ASC
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
Expand All @@ -24,6 +26,8 @@ import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.sql.update
import suwayomi.tachidesk.manga.impl.Manga.getManga
import suwayomi.tachidesk.manga.impl.download.DownloadManager
import suwayomi.tachidesk.manga.impl.download.DownloadManager.EnqueueInput
import suwayomi.tachidesk.manga.impl.util.lang.awaitSingle
import suwayomi.tachidesk.manga.impl.util.source.GetCatalogueSource.getCatalogueSourceOrStub
import suwayomi.tachidesk.manga.model.dataclass.ChapterDataClass
Expand All @@ -35,9 +39,12 @@ import suwayomi.tachidesk.manga.model.table.ChapterTable
import suwayomi.tachidesk.manga.model.table.MangaTable
import suwayomi.tachidesk.manga.model.table.PageTable
import suwayomi.tachidesk.manga.model.table.toDataClass
import suwayomi.tachidesk.server.serverConfig
import java.time.Instant

object Chapter {
private val logger = KotlinLogging.logger { }

/** get chapter list when showing a manga */
suspend fun getChapterList(mangaId: Int, onlineFetch: Boolean = false): List<ChapterDataClass> {
return if (onlineFetch) {
Expand All @@ -55,6 +62,10 @@ object Chapter {
}
}

fun getCountOfMangaChapters(mangaId: Int): Int {
return transaction { ChapterTable.select { ChapterTable.manga eq mangaId }.count().toInt() }
}

private suspend fun getSourceChapters(mangaId: Int): List<ChapterDataClass> {
val chapterList = fetchChapterList(mangaId)

Expand Down Expand Up @@ -106,6 +117,7 @@ object Chapter {
url = manga.url
}

val numberOfCurrentChapters = getCountOfMangaChapters(mangaId)
val chapterList = source.fetchChapterList(sManga).awaitSingle()

// Recognize number for new chapters.
Expand Down Expand Up @@ -157,8 +169,13 @@ object Chapter {
}
}

val newChapters = transaction {
ChapterTable.select { ChapterTable.manga eq mangaId }
.orderBy(ChapterTable.sourceOrder to SortOrder.DESC).toList()
}

// clear any orphaned/duplicate chapters that are in the db but not in `chapterList`
val dbChapterCount = transaction { ChapterTable.select { ChapterTable.manga eq mangaId }.count() }
val dbChapterCount = newChapters.count()
if (dbChapterCount > chapterList.size) { // we got some clean up due
val dbChapterList = transaction {
ChapterTable.select { ChapterTable.manga eq mangaId }
Expand All @@ -179,9 +196,39 @@ object Chapter {
}
}

downloadNewChapters(mangaId, numberOfCurrentChapters, newChapters)

return chapterList
}

fun downloadNewChapters(mangaId: Int, prevNumberOfChapters: Int, newChapters: List<ResultRow>) {
// convert numbers to be index based
val currentNumberOfChapters = (prevNumberOfChapters - 1).coerceAtLeast(0)
val updatedNumberOfChapters = (newChapters.size - 1).coerceAtLeast(0)

val areNewChaptersAvailable = currentNumberOfChapters < updatedNumberOfChapters
val wasInitialFetch = currentNumberOfChapters == 0

// make sure to ignore initial fetch
val downloadNewChapters = serverConfig.autoDownloadNewChapters && !wasInitialFetch && areNewChaptersAvailable
if (!downloadNewChapters) {
return
}

val numberOfNewChapters = updatedNumberOfChapters - currentNumberOfChapters
val chapterIdsToDownload = newChapters.subList(0, numberOfNewChapters)
.filter { !it[ChapterTable.isRead] && !it[ChapterTable.isDownloaded] }.map { it[ChapterTable.id].value }

if (chapterIdsToDownload.isEmpty()) {
return
}

logger.info { "downloadNewChapters($mangaId): Downloading \"${chapterIdsToDownload.size}\" new chapter(s)..." }

DownloadManager.enqueue(EnqueueInput(chapterIdsToDownload))
DownloadManager.start()
}

fun modifyChapter(
mangaId: Int,
chapterIndex: Int,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ object DownloadManager {
scope.launch {
downloaderWatch.sample(1.seconds).collect {
val runningDownloaders = downloaders.values.filter { it.isActive }
logger.info { "Running: ${runningDownloaders.size}" }
logger.info { "Running: ${runningDownloaders.size}, Queued: ${downloadQueue.size}" }
if (runningDownloaders.size < MAX_SOURCES_IN_PARAllEL) {
downloadQueue.asSequence()
.map { it.manga.sourceId.toLong() }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class ServerConfig(getConfig: () -> Config, moduleName: String = MODULE_NAME) :
// downloader
var downloadAsCbz: Boolean by overridableConfig
var downloadsPath: String by overridableConfig
var autoDownloadNewChapters: Boolean by overridableConfig

// updater
var maxParallelUpdateRequests: Int by overridableConfig
Expand Down
1 change: 1 addition & 0 deletions server/src/main/resources/server-reference.conf
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ server.electronPath = ""
# downloader
server.downloadAsCbz = false
server.downloadsPath = ""
server.autoDownloadNewChapters = false # if new chapters that have been retrieved should get automatically downloaded

# updater
server.maxParallelUpdateRequests = 10 # sets how many sources can be updated in parallel. updates are grouped by source and all mangas of a source are updated synchronously
Expand Down
1 change: 1 addition & 0 deletions server/src/test/resources/server-reference.conf
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ server.socksProxyPort = ""

# downloader
server.downloadAsCbz = false
server.autoDownloadNewChapters = false

# updater
server.maxParallelUpdateRequests = 10
Expand Down

0 comments on commit 8690e91

Please sign in to comment.