diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/online/all/MangaDex.kt b/app/src/main/java/eu/kanade/tachiyomi/source/online/all/MangaDex.kt index 65e271bae..9a593267f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/source/online/all/MangaDex.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/source/online/all/MangaDex.kt @@ -56,7 +56,7 @@ import kotlin.reflect.KClass @Suppress("OverridingDeprecatedMember") class MangaDex(delegate: HttpSource, val context: Context) : DelegatedHttpSource(delegate), - MetadataSource, + MetadataSource>>, UrlImportableSource, FollowsSource, LoginSource, @@ -193,8 +193,8 @@ class MangaDex(delegate: HttpSource, val context: Context) : return MangaDexDescriptionAdapter(controller) } - override suspend fun parseIntoMetadata(metadata: MangaDexSearchMetadata, input: MangaDto) { - apiMangaParser.parseIntoMetadata(metadata, input) + override suspend fun parseIntoMetadata(metadata: MangaDexSearchMetadata, input: Pair>) { + apiMangaParser.parseIntoMetadata(metadata, input.first, input.second) } // LoginSource methods diff --git a/app/src/main/java/exh/md/dto/MangaDto.kt b/app/src/main/java/exh/md/dto/MangaDto.kt index 58c746e69..8e60fc30d 100644 --- a/app/src/main/java/exh/md/dto/MangaDto.kt +++ b/app/src/main/java/exh/md/dto/MangaDto.kt @@ -122,3 +122,22 @@ data class CoverDataDto( data class CoverAttributesDto( val fileName: String, ) + +@Serializable +data class AggregateDto( + val result: String, + val volumes: Map, +) + +@Serializable +data class AggregateVolume( + val volume: String, + val count: String, + val chapters: Map, +) + +@Serializable +data class AggregateChapter( + val chapter: String, + val count: String, +) diff --git a/app/src/main/java/exh/md/handlers/ApiMangaParser.kt b/app/src/main/java/exh/md/handlers/ApiMangaParser.kt index df25053f3..33133e4fe 100644 --- a/app/src/main/java/exh/md/handlers/ApiMangaParser.kt +++ b/app/src/main/java/exh/md/handlers/ApiMangaParser.kt @@ -36,14 +36,14 @@ class ApiMangaParser( }?.call() ?: error("Could not find no-args constructor for meta class: ${metaClass.qualifiedName}!") - fun parseToManga(manga: MangaInfo, input: MangaDto, sourceId: Long): MangaInfo { + fun parseToManga(manga: MangaInfo, input: MangaDto, simpleChapters: List, sourceId: Long): MangaInfo { val mangaId = db.getManga(manga.key, sourceId).executeAsBlocking()?.id val metadata = if (mangaId != null) { val flatMetadata = db.getFlatMetadataForManga(mangaId).executeAsBlocking() flatMetadata?.raise(metaClass) ?: newMetaInstance() } else newMetaInstance() - parseIntoMetadata(metadata, input) + parseIntoMetadata(metadata, input, simpleChapters) if (mangaId != null) { metadata.mangaId = mangaId db.insertFlatMetadata(metadata.flatten()) @@ -52,7 +52,7 @@ class ApiMangaParser( return metadata.createMangaInfo(manga) } - fun parseIntoMetadata(metadata: MangaDexSearchMetadata, mangaDto: MangaDto) { + fun parseIntoMetadata(metadata: MangaDexSearchMetadata, mangaDto: MangaDto, simpleChapters: List) { with(metadata) { try { val mangaAttributesDto = mangaDto.data.attributes @@ -73,11 +73,11 @@ class ApiMangaParser( authors = mangaRelationshipsDto.filter { relationshipDto -> relationshipDto.type.equals(MdConstants.Types.author, true) - }.mapNotNull { it.attributes!!.name }.distinct() + }.mapNotNull { it.attributes?.name }.distinct() artists = mangaRelationshipsDto.filter { relationshipDto -> relationshipDto.type.equals(MdConstants.Types.artist, true) - }.mapNotNull { it.attributes!!.name }.distinct() + }.mapNotNull { it.attributes?.name }.distinct() langFlag = mangaAttributesDto.originalLanguage val lastChapter = mangaAttributesDto.lastChapter?.toFloatOrNull() @@ -98,15 +98,17 @@ class ApiMangaParser( // val filteredChapters = filterChapterForChecking(networkApiManga) - val tempStatus = parseStatus(mangaAttributesDto.status ?: "") - /*val publishedOrCancelled = - tempStatus == SManga.PUBLICATION_COMPLETE || tempStatus == SManga.CANCELLED - if (publishedOrCancelled && isMangaCompleted(networkApiManga, filteredChapters)) { - manga.status = SManga.COMPLETED - manga.missing_chapters = null - } else {*/ - status = tempStatus - // } + val tempStatus = parseStatus(mangaAttributesDto.status) + val publishedOrCancelled = tempStatus == SManga.PUBLICATION_COMPLETE || tempStatus == SManga.CANCELLED + status = if ( + mangaAttributesDto.lastChapter != null && + publishedOrCancelled && + mangaAttributesDto.lastChapter in simpleChapters + ) { + SManga.COMPLETED + } else { + tempStatus + } // things that will go with the genre tags but aren't actually genre val nonGenres = listOfNotNull( @@ -134,34 +136,6 @@ class ApiMangaParser( } } - /** - * If chapter title is oneshot or a chapter exists which matches the last chapter in the required language - * return manga is complete - */ - /*private fun isMangaCompleted( - serializer: ApiMangaSerializer, - filteredChapters: List - ): Boolean { - if (filteredChapters.isEmpty() || serializer.data.manga.lastChapter.isNullOrEmpty()) { - return false - } - val finalChapterNumber = serializer.data.manga.lastChapter!! - if (MdUtil.validOneShotFinalChapters.contains(finalChapterNumber)) { - filteredChapters.firstOrNull()?.let { - if (isOneShot(it, finalChapterNumber)) { - return true - } - } - } - val removeOneshots = filteredChapters.asSequence() - .map { it.chapter!!.toDoubleOrNull() } - .filter { it != null } - .map { floor(it!!).toInt() } - .filter { it != 0 } - .toList().distinctBy { it } - return removeOneshots.toList().size == floor(finalChapterNumber.toDouble()).toInt() - }*/ - /* private fun filterChapterForChecking(serializer: ApiMangaSerializer): List { serializer.data.chapters ?: return emptyList() return serializer.data.chapters.asSequence() @@ -182,7 +156,7 @@ class ApiMangaParser( ((chapter.chapter.isNullOrEmpty() || chapter.chapter == "0") && MdUtil.validOneShotFinalChapters.contains(finalChapterNumber)) }*/ - private fun parseStatus(status: String) = when (status) { + private fun parseStatus(status: String?) = when (status) { "ongoing" -> SManga.ONGOING "completed" -> SManga.PUBLICATION_COMPLETE "cancelled" -> SManga.CANCELLED diff --git a/app/src/main/java/exh/md/handlers/MangaHandler.kt b/app/src/main/java/exh/md/handlers/MangaHandler.kt index b5324f978..d79622bd8 100644 --- a/app/src/main/java/exh/md/handlers/MangaHandler.kt +++ b/app/src/main/java/exh/md/handlers/MangaHandler.kt @@ -14,6 +14,7 @@ import exh.md.utils.MdConstants import exh.md.utils.MdUtil import exh.md.utils.mdListCall import exh.metadata.metadata.MangaDexSearchMetadata +import kotlinx.coroutines.CancellationException import kotlinx.coroutines.async import rx.Observable import tachiyomi.source.model.ChapterInfo @@ -27,7 +28,8 @@ class MangaHandler( ) { suspend fun getMangaDetails(manga: MangaInfo, sourceId: Long, forceLatestCovers: Boolean): MangaInfo { val response = withIOContext { service.viewManga(MdUtil.getMangaId(manga.key)) } - return apiMangaParser.parseToManga(manga, response, sourceId) + val simpleChapters = withIOContext { getSimpleChapters(manga) } + return apiMangaParser.parseToManga(manga, response, simpleChapters, sourceId) } fun fetchMangaDetailsObservable(manga: SManga, sourceId: Long, forceLatestCovers: Boolean): Observable { @@ -88,4 +90,17 @@ class MangaHandler( apiMangaParser.chapterParseForMangaId(service.viewChapter(chapterId)) } } + + private suspend fun getSimpleChapters(manga: MangaInfo): List { + return runCatching { service.aggregateChapters(MdUtil.getMangaId(manga.key), lang) } + .onFailure { + if (it is CancellationException) throw it + } + .map { dto -> + dto.volumes.values + .flatMap { it.chapters.values } + .map { it.chapter } + } + .getOrDefault(emptyList()) + } } diff --git a/app/src/main/java/exh/md/service/MangaDexService.kt b/app/src/main/java/exh/md/service/MangaDexService.kt index a14cfb4f0..304e4b1b9 100644 --- a/app/src/main/java/exh/md/service/MangaDexService.kt +++ b/app/src/main/java/exh/md/service/MangaDexService.kt @@ -4,6 +4,7 @@ import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.network.await import eu.kanade.tachiyomi.network.parseAs +import exh.md.dto.AggregateDto import exh.md.dto.AtHomeDto import exh.md.dto.AtHomeImageReportDto import exh.md.dto.ChapterDto @@ -64,6 +65,26 @@ class MangaDexService( ).await().parseAs(MdUtil.jsonParser) } + suspend fun aggregateChapters( + id: String, + translatedLanguage: String + ): AggregateDto { + return client.newCall( + GET( + MdApi.manga.toHttpUrl() + .newBuilder() + .apply { + addPathSegment(id) + addPathSegment("aggregate") + addQueryParameter("translatedLanguage[]", translatedLanguage) + } + .build() + .toString(), + cache = CacheControl.FORCE_NETWORK + ) + ).await().parseAs(MdUtil.jsonParser) + } + suspend fun viewChapters( id: String, translatedLanguage: String,