Feat: Adds OPDS Chapter Filtering/Ordering (#1392)

* Adds server level configs for OPDS

* PR comments

* Refactor server-reference.conf (itemsPerPage range)

* Coerce itemsPerPage (10, 5000) and default invalid sort orders to DESC

* Coerce itemsPerPage (10, 5000) and default invalid sort orders to DESC

* Change opdsChapterSortOrder type to Enum(SortOrder)

* Fix serialization of SortOrderEnum & Add `opdsShowOnlyDownloadedChapters` config
This commit is contained in:
Shirish
2025-05-23 05:27:55 +05:30
committed by GitHub
parent 814e4ba744
commit 0405a535c7
11 changed files with 192 additions and 67 deletions

View File

@@ -9,6 +9,7 @@ package suwayomi.tachidesk.manga
import io.javalin.apibuilder.ApiBuilder.delete
import io.javalin.apibuilder.ApiBuilder.get
import io.javalin.apibuilder.ApiBuilder.head
import io.javalin.apibuilder.ApiBuilder.patch
import io.javalin.apibuilder.ApiBuilder.path
import io.javalin.apibuilder.ApiBuilder.post
@@ -83,6 +84,7 @@ object MangaAPI {
path("chapter") {
post("batch", MangaController.anyChapterBatch)
get("{chapterId}/download", MangaController.downloadChapter)
head("{chapterId}/download", MangaController.downloadChapter)
}
path("category") {

View File

@@ -7,6 +7,7 @@ package suwayomi.tachidesk.manga.controller
* 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 io.javalin.http.HandlerType
import io.javalin.http.HttpStatus
import kotlinx.serialization.json.Json
import suwayomi.tachidesk.manga.impl.CategoryManga
@@ -434,20 +435,27 @@ object MangaController {
val downloadChapter =
handler(
pathParam<Int>("chapterId"),
queryParam<Boolean?>("markAsRead"),
documentWith = {
withOperation {
summary("Download chapter as CBZ")
description("Get the CBZ file of the specified chapter")
}
},
behaviorOf = { ctx, chapterId ->
behaviorOf = { ctx, chapterId, markAsRead ->
val shouldMarkAsRead = if (ctx.method() == HandlerType.HEAD) false else markAsRead
ctx.future {
future { ChapterDownloadHelper.getCbzForDownload(chapterId) }
future { ChapterDownloadHelper.getCbzForDownload(chapterId, shouldMarkAsRead) }
.thenApply { (inputStream, fileName, fileSize) ->
ctx.header("Content-Type", "application/vnd.comicbook+zip")
ctx.header("Content-Disposition", "attachment; filename=\"$fileName\"")
ctx.header("Content-Length", fileSize.toString())
ctx.result(inputStream)
if (ctx.method() == HandlerType.HEAD) {
inputStream.close()
ctx.status(200)
} else {
ctx.result(inputStream)
}
}
}
},

View File

@@ -57,7 +57,10 @@ object ChapterDownloadHelper {
chapterId: Int,
): Pair<InputStream, Long> = provider(mangaId, chapterId).getAsArchiveStream()
fun getCbzForDownload(chapterId: Int): Triple<InputStream, String, Long> {
fun getCbzForDownload(
chapterId: Int,
markAsRead: Boolean?,
): Triple<InputStream, String, Long> {
val (chapterData, mangaTitle) =
transaction {
val row =
@@ -74,6 +77,17 @@ object ChapterDownloadHelper {
val cbzFile = provider(chapterData.mangaId, chapterData.id).getAsArchiveStream()
if (markAsRead == true) {
Chapter.modifyChapter(
chapterData.mangaId,
chapterData.index,
isRead = true,
isBookmarked = null,
markPrevRead = null,
lastPageRead = null,
)
}
return Triple(cbzFile.first, fileName, cbzFile.second)
}
}