diff --git a/src/all/komga/CHANGELOG.md b/src/all/komga/CHANGELOG.md index 510eb4e68..a383e6550 100644 --- a/src/all/komga/CHANGELOG.md +++ b/src/all/komga/CHANGELOG.md @@ -1,3 +1,12 @@ +## 1.2.21 + +Minimum Komga version required: `0.75.0` + +### Features + +* browse read lists (from the filter menu) +* filter by collection, respecting the collection's ordering + ## 1.2.20 Minimum Komga version required: `0.75.0` diff --git a/src/all/komga/build.gradle b/src/all/komga/build.gradle index 8b64df89c..81da679c5 100644 --- a/src/all/komga/build.gradle +++ b/src/all/komga/build.gradle @@ -5,7 +5,7 @@ ext { extName = 'Komga' pkgNameSuffix = 'all.komga' extClass = '.KomgaFactory' - extVersionCode = 20 + extVersionCode = 21 libVersion = '1.2' } diff --git a/src/all/komga/src/eu/kanade/tachiyomi/extension/all/komga/Komga.kt b/src/all/komga/src/eu/kanade/tachiyomi/extension/all/komga/Komga.kt index edf62585e..cab017162 100644 --- a/src/all/komga/src/eu/kanade/tachiyomi/extension/all/komga/Komga.kt +++ b/src/all/komga/src/eu/kanade/tachiyomi/extension/all/komga/Komga.kt @@ -15,6 +15,7 @@ import eu.kanade.tachiyomi.extension.all.komga.dto.CollectionDto import eu.kanade.tachiyomi.extension.all.komga.dto.LibraryDto import eu.kanade.tachiyomi.extension.all.komga.dto.PageDto import eu.kanade.tachiyomi.extension.all.komga.dto.PageWrapperDto +import eu.kanade.tachiyomi.extension.all.komga.dto.ReadListDto import eu.kanade.tachiyomi.extension.all.komga.dto.SeriesDto import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.source.ConfigurableSource @@ -54,7 +55,17 @@ open class Komga(suffix: String = "") : ConfigurableSource, HttpSource() { processSeriesPage(response) override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { - val url = HttpUrl.parse("$baseUrl/api/v1/series?search=$query&page=${page - 1}")!!.newBuilder() + val collectionId = (filters.find { it is CollectionSelect } as? CollectionSelect)?.let { + it.values[it.state].id + } + + val type = when { + collectionId != null -> "collections/$collectionId/series" + filters.find { it is TypeSelect }?.state == 1 -> "readlists" + else -> "series" + } + + val url = HttpUrl.parse("$baseUrl/api/v1/$type?search=$query&page=${page - 1}")!!.newBuilder() filters.forEach { filter -> when (filter) { @@ -74,17 +85,6 @@ open class Komga(suffix: String = "") : ConfigurableSource, HttpSource() { url.addQueryParameter("library_id", libraryToInclude.joinToString(",")) } } - is CollectionGroup -> { - val collectionToInclude = mutableListOf() - filter.state.forEach { content -> - if (content.state) { - collectionToInclude.add(content.id) - } - } - if (collectionToInclude.isNotEmpty()) { - url.addQueryParameter("collection_id", collectionToInclude.joinToString(",")) - } - } is StatusGroup -> { val statusToInclude = mutableListOf() filter.state.forEach { content -> @@ -164,10 +164,14 @@ open class Komga(suffix: String = "") : ConfigurableSource, HttpSource() { override fun mangaDetailsRequest(manga: SManga): Request = GET(baseUrl + manga.url, headers) - override fun mangaDetailsParse(response: Response): SManga { - val series = gson.fromJson(response.body()?.charStream()!!) - return series.toSManga() - } + override fun mangaDetailsParse(response: Response): SManga = + if (response.fromReadList()) { + val readList = gson.fromJson(response.body()?.charStream()!!) + readList.toSManga() + } else { + val series = gson.fromJson(response.body()?.charStream()!!) + series.toSManga() + } override fun chapterListRequest(manga: SManga): Request = GET("$baseUrl${manga.url}/books?unpaged=true&media_status=READY", headers) @@ -175,15 +179,16 @@ open class Komga(suffix: String = "") : ConfigurableSource, HttpSource() { override fun chapterListParse(response: Response): List { val page = gson.fromJson>(response.body()?.charStream()!!).content - return page.map { book -> + val r = page.map { book -> SChapter.create().apply { chapter_number = book.metadata.numberSort - name = "${book.metadata.number} - ${book.metadata.title} (${book.size})" + name = "${if (!response.fromReadList()) "${book.metadata.number} - " else ""}${book.metadata.title} (${book.size})" url = "$baseUrl/api/v1/books/${book.id}" date_upload = book.metadata.releaseDate?.let { parseDate(it) } ?: parseDateTime(book.fileLastModified) } - }.sortedByDescending { it.chapter_number } + } + return if (!response.fromReadList()) r.sortedByDescending { it.chapter_number } else r.reversed() } override fun pageListRequest(chapter: SChapter): Request = @@ -206,15 +211,15 @@ open class Komga(suffix: String = "") : ConfigurableSource, HttpSource() { } private fun processSeriesPage(response: Response): MangasPage { - var lastPage: Boolean - val page = with(gson.fromJson>(response.body()?.charStream()!!)) { - lastPage = last - content + if (response.fromReadList()) { + with(gson.fromJson>(response.body()?.charStream()!!)) { + return MangasPage(content.map { it.toSManga() }, !last) + } + } else { + with(gson.fromJson>(response.body()?.charStream()!!)) { + return MangasPage(content.map { it.toSManga() }, !last) + } } - val mangas = page.map { - it.toSManga() - } - return MangasPage(mangas, !lastPage) } private fun SeriesDto.toSManga(): SManga = @@ -229,12 +234,22 @@ open class Komga(suffix: String = "") : ConfigurableSource, HttpSource() { } genre = (metadata.genres + metadata.tags).joinToString(", ") description = metadata.summary.ifBlank { booksMetadata.summary } - booksMetadata.authors.groupBy { it.role }.let { - author = it["writer"]?.map { it.name }?.distinct()?.joinToString() - artist = it["penciller"]?.map { it.name }?.distinct()?.joinToString() + booksMetadata.authors.groupBy { it.role }.let { map -> + author = map["writer"]?.map { it.name }?.distinct()?.joinToString() + artist = map["penciller"]?.map { it.name }?.distinct()?.joinToString() } } + private fun ReadListDto.toSManga(): SManga = + SManga.create().apply { + title = name + url = "/api/v1/readlists/$id" + thumbnail_url = "$baseUrl/api/v1/readlists/$id/thumbnail" + status = SManga.UNKNOWN + } + + private fun Response.fromReadList() = request().url().toString().contains("/api/v1/readlists") + private fun parseDate(date: String?): Long = if (date == null) Date().time @@ -263,10 +278,10 @@ open class Komga(suffix: String = "") : ConfigurableSource, HttpSource() { override fun imageUrlParse(response: Response): String = "" + private class TypeSelect : Filter.Select("Search for", arrayOf(TYPE_SERIES, TYPE_READLISTS)) private class LibraryFilter(val id: String, name: String) : Filter.CheckBox(name, false) private class LibraryGroup(libraries: List) : Filter.Group("Libraries", libraries) - private class CollectionFilter(val id: String, name: String) : Filter.CheckBox(name, false) - private class CollectionGroup(collections: List) : Filter.Group("Collections", collections) + private class CollectionSelect(collections: List) : Filter.Select("Collection", collections.toTypedArray()) private class SeriesSort : Filter.Sort("Sort", arrayOf("Alphabetically", "Date added", "Date updated"), Selection(0, true)) private class StatusFilter(name: String) : Filter.CheckBox(name, false) private class StatusGroup(filters: List) : Filter.Group("Status", filters) @@ -280,11 +295,19 @@ open class Komga(suffix: String = "") : ConfigurableSource, HttpSource() { private class AuthorFilter(val author: AuthorDto) : Filter.CheckBox(author.name, false) private class AuthorGroup(role: String, authors: List) : Filter.Group(role, authors) + private data class CollectionFilterEntry( + val name: String, + val id: String? = null + ) { + override fun toString() = name + } + override fun getFilterList(): FilterList = FilterList( UnreadOnly(), + TypeSelect(), + CollectionSelect(listOf(CollectionFilterEntry("None")) + collections.map { CollectionFilterEntry(it.name, it.id) }), LibraryGroup(libraries.map { LibraryFilter(it.id, it.name) }.sortedBy { it.name.toLowerCase() }), - CollectionGroup(collections.map { CollectionFilter(it.id, it.name) }.sortedBy { it.name.toLowerCase() }), StatusGroup(listOf("Ongoing", "Ended", "Abandoned", "Hiatus").map { StatusFilter(it) }), GenreGroup(genres.map { GenreFilter(it) }), TagGroup(tags.map { TagFilter(it) }), @@ -484,7 +507,7 @@ open class Komga(suffix: String = "") : ConfigurableSource, HttpSource() { { response -> authors = try { val list: List = gson.fromJson(response.body()?.charStream()!!) - list.groupBy({ it.role }, { it }) + list.groupBy { it.role } } catch (e: Exception) { emptyMap() } @@ -502,5 +525,8 @@ open class Komga(suffix: String = "") : ConfigurableSource, HttpSource() { private const val PASSWORD_DEFAULT = "" private val supportedImageTypes = listOf("image/jpeg", "image/png", "image/gif", "image/webp") + + private const val TYPE_SERIES = "Series" + private const val TYPE_READLISTS = "Read lists" } } diff --git a/src/all/komga/src/eu/kanade/tachiyomi/extension/all/komga/dto/Dto.kt b/src/all/komga/src/eu/kanade/tachiyomi/extension/all/komga/dto/Dto.kt index efb77222d..41a2c4cfb 100644 --- a/src/all/komga/src/eu/kanade/tachiyomi/extension/all/komga/dto/Dto.kt +++ b/src/all/komga/src/eu/kanade/tachiyomi/extension/all/komga/dto/Dto.kt @@ -104,3 +104,12 @@ data class CollectionDto( val lastModifiedDate: String, val filtered: Boolean ) + +data class ReadListDto( + val id: String, + val name: String, + val bookIds: List, + val createdDate: String, + val lastModifiedDate: String, + val filtered: Boolean +)