From d7856fe3510a177477d0d771ad6a423008e10fd5 Mon Sep 17 00:00:00 2001 From: Jobobby04 Date: Mon, 27 Dec 2021 16:18:02 -0500 Subject: [PATCH] Mangadex support manga rating --- .../tachiyomi/source/online/all/MangaDex.kt | 11 ++++---- app/src/main/java/exh/md/dto/StatisticsDto.kt | 18 ++++++++++++ .../java/exh/md/handlers/ApiMangaParser.kt | 28 +++++++++++++------ .../main/java/exh/md/handlers/MangaHandler.kt | 18 ++++++++++-- .../java/exh/md/service/MangaDexService.kt | 20 +++++++++++++ app/src/main/java/exh/md/utils/MdApi.kt | 1 + .../main/java/exh/md/utils/MdExtensions.kt | 7 ++--- .../metadata/MangaDexSearchMetadata.kt | 2 +- .../adapters/MangaDexDescriptionAdapter.kt | 11 +++++--- .../res/layout/description_adapter_md.xml | 9 ++++-- 10 files changed, 98 insertions(+), 27 deletions(-) create mode 100644 app/src/main/java/exh/md/dto/StatisticsDto.kt 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 285690183..f40a365fd 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 @@ -26,6 +26,7 @@ import eu.kanade.tachiyomi.ui.manga.MangaController import eu.kanade.tachiyomi.util.lang.runAsObservable import exh.md.MangaDexFabHeaderAdapter import exh.md.dto.MangaDto +import exh.md.dto.StatisticsMangaDto import exh.md.handlers.ApiMangaParser import exh.md.handlers.BilibiliHandler import exh.md.handlers.ComikeyHandler @@ -58,7 +59,7 @@ import kotlin.reflect.KClass @Suppress("OverridingDeprecatedMember") class MangaDex(delegate: HttpSource, val context: Context) : DelegatedHttpSource(delegate), - MetadataSource>>, + MetadataSource, StatisticsMangaDto>>, UrlImportableSource, FollowsSource, LoginSource, @@ -206,8 +207,8 @@ class MangaDex(delegate: HttpSource, val context: Context) : return MangaDexDescriptionAdapter(controller) } - override suspend fun parseIntoMetadata(metadata: MangaDexSearchMetadata, input: Pair>) { - apiMangaParser.parseIntoMetadata(metadata, input.first, input.second) + override suspend fun parseIntoMetadata(metadata: MangaDexSearchMetadata, input: Triple, StatisticsMangaDto>) { + apiMangaParser.parseIntoMetadata(metadata, input.first, input.second, input.third) } // LoginSource methods @@ -265,11 +266,11 @@ class MangaDex(delegate: HttpSource, val context: Context) : // Tracker methods /*suspend fun updateReadingProgress(track: Track): Boolean { return followsHandler.updateReadingProgress(track) - } + }*/ suspend fun updateRating(track: Track): Boolean { return followsHandler.updateRating(track) - }*/ + } suspend fun getTrackingAndMangaInfo(track: Track): Pair { return mangaHandler.getTrackingInfo(track) diff --git a/app/src/main/java/exh/md/dto/StatisticsDto.kt b/app/src/main/java/exh/md/dto/StatisticsDto.kt new file mode 100644 index 000000000..3081c2531 --- /dev/null +++ b/app/src/main/java/exh/md/dto/StatisticsDto.kt @@ -0,0 +1,18 @@ +package exh.md.dto + +import kotlinx.serialization.Serializable + +@Serializable +data class StatisticsDto( + val statistics: Map +) + +@Serializable +data class StatisticsMangaDto( + val rating: StatisticsMangaRatingDto +) + +@Serializable +data class StatisticsMangaRatingDto( + val average: Double? +) diff --git a/app/src/main/java/exh/md/handlers/ApiMangaParser.kt b/app/src/main/java/exh/md/handlers/ApiMangaParser.kt index 0d3d884d2..eebdf6a51 100644 --- a/app/src/main/java/exh/md/handlers/ApiMangaParser.kt +++ b/app/src/main/java/exh/md/handlers/ApiMangaParser.kt @@ -6,6 +6,7 @@ import exh.log.xLogE import exh.md.dto.ChapterDataDto import exh.md.dto.ChapterDto import exh.md.dto.MangaDto +import exh.md.dto.StatisticsMangaDto import exh.md.utils.MdConstants import exh.md.utils.MdUtil import exh.md.utils.asMdMap @@ -36,14 +37,20 @@ class ApiMangaParser( }?.call() ?: error("Could not find no-args constructor for meta class: ${metaClass.qualifiedName}!") - fun parseToManga(manga: MangaInfo, input: MangaDto, simpleChapters: List, sourceId: Long): MangaInfo { + fun parseToManga( + manga: MangaInfo, + sourceId: Long, + input: MangaDto, + simpleChapters: List, + statistics: StatisticsMangaDto? + ): 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, simpleChapters) + parseIntoMetadata(metadata, input, simpleChapters, statistics) if (mangaId != null) { metadata.mangaId = mangaId db.insertFlatMetadata(metadata.flatten()) @@ -52,7 +59,12 @@ class ApiMangaParser( return metadata.createMangaInfo(manga) } - fun parseIntoMetadata(metadata: MangaDexSearchMetadata, mangaDto: MangaDto, simpleChapters: List) { + fun parseIntoMetadata( + metadata: MangaDexSearchMetadata, + mangaDto: MangaDto, + simpleChapters: List, + statistics: StatisticsMangaDto? + ) { with(metadata) { try { val mangaAttributesDto = mangaDto.data.attributes @@ -83,12 +95,12 @@ class ApiMangaParser( val lastChapter = mangaAttributesDto.lastChapter?.toFloatOrNull() lastChapterNumber = lastChapter?.floor() - /*networkManga.rating?.let { - manga.rating = it.bayesian ?: it.mean - manga.users = it.users - }*/ + statistics?.rating?.let { + rating = it.average?.toFloat() + // manga.users = it.users + } - mangaAttributesDto.links?.asMdMap()?.let { links -> + mangaAttributesDto.links?.asMdMap()?.let { links -> links["al"]?.let { anilistId = it } links["kt"]?.let { kitsuId = it } links["mal"]?.let { myAnimeListId = it } diff --git a/app/src/main/java/exh/md/handlers/MangaHandler.kt b/app/src/main/java/exh/md/handlers/MangaHandler.kt index 216fb6f5a..fe7002d00 100644 --- a/app/src/main/java/exh/md/handlers/MangaHandler.kt +++ b/app/src/main/java/exh/md/handlers/MangaHandler.kt @@ -15,7 +15,9 @@ import exh.md.utils.MdUtil import exh.md.utils.mdListCall import exh.metadata.metadata.MangaDexSearchMetadata import kotlinx.coroutines.CancellationException +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.async +import kotlinx.coroutines.coroutineScope import rx.Observable import tachiyomi.source.model.ChapterInfo import tachiyomi.source.model.MangaInfo @@ -27,9 +29,19 @@ class MangaHandler( private val followsHandler: FollowsHandler ) { suspend fun getMangaDetails(manga: MangaInfo, sourceId: Long): MangaInfo { - val response = withIOContext { service.viewManga(MdUtil.getMangaId(manga.key)) } - val simpleChapters = withIOContext { getSimpleChapters(manga) } - return apiMangaParser.parseToManga(manga, response, simpleChapters, sourceId) + return coroutineScope { + val mangaId = MdUtil.getMangaId(manga.key) + val response = async(Dispatchers.IO) { service.viewManga(mangaId) } + val simpleChapters = async(Dispatchers.IO) { getSimpleChapters(manga) } + val statistics = async(Dispatchers.IO) { service.mangasRating(mangaId).statistics[mangaId] } + apiMangaParser.parseToManga( + manga, + sourceId, + response.await(), + simpleChapters.await(), + statistics.await() + ) + } } fun fetchMangaDetailsObservable(manga: SManga, sourceId: Long): Observable { diff --git a/app/src/main/java/exh/md/service/MangaDexService.kt b/app/src/main/java/exh/md/service/MangaDexService.kt index f324f92a8..2ec8b193a 100644 --- a/app/src/main/java/exh/md/service/MangaDexService.kt +++ b/app/src/main/java/exh/md/service/MangaDexService.kt @@ -13,6 +13,7 @@ import exh.md.dto.MangaDto import exh.md.dto.MangaListDto import exh.md.dto.RelationListDto import exh.md.dto.ResultDto +import exh.md.dto.StatisticsDto import exh.md.utils.MdApi import exh.md.utils.MdConstants import exh.md.utils.MdUtil @@ -66,6 +67,25 @@ class MangaDexService( ).await().parseAs(MdUtil.jsonParser) } + suspend fun mangasRating( + vararg ids: String + ): StatisticsDto { + return client.newCall( + GET( + MdApi.statistics.toHttpUrl() + .newBuilder() + .apply { + ids.forEach { id -> + addQueryParameter("manga[]", id) + } + } + .build() + .toString(), + cache = CacheControl.FORCE_NETWORK + ) + ).await().parseAs(MdUtil.jsonParser) + } + suspend fun aggregateChapters( id: String, translatedLanguage: String diff --git a/app/src/main/java/exh/md/utils/MdApi.kt b/app/src/main/java/exh/md/utils/MdApi.kt index e02144a5b..9b1c4b921 100644 --- a/app/src/main/java/exh/md/utils/MdApi.kt +++ b/app/src/main/java/exh/md/utils/MdApi.kt @@ -10,6 +10,7 @@ object MdApi { const val chapter = "$baseUrl/chapter" const val group = "$baseUrl/group" const val author = "$baseUrl/author" + const val statistics = "$baseUrl/statistics/manga" const val chapterImageServer = "$baseUrl/at-home/server" const val userFollows = "$baseUrl/user/follows/manga" const val readingStatusForAllManga = "$baseUrl/manga/status" diff --git a/app/src/main/java/exh/md/utils/MdExtensions.kt b/app/src/main/java/exh/md/utils/MdExtensions.kt index dba93f2ec..a4d05e291 100644 --- a/app/src/main/java/exh/md/utils/MdExtensions.kt +++ b/app/src/main/java/exh/md/utils/MdExtensions.kt @@ -3,9 +3,8 @@ package exh.md.utils import exh.md.dto.ListCallDto import exh.util.under import kotlinx.serialization.json.JsonElement -import kotlinx.serialization.json.contentOrNull +import kotlinx.serialization.json.decodeFromJsonElement import kotlinx.serialization.json.jsonObject -import kotlinx.serialization.json.jsonPrimitive suspend fun mdListCall(request: suspend (offset: Int) -> ListCallDto): List { val results = mutableListOf() @@ -20,8 +19,8 @@ suspend fun mdListCall(request: suspend (offset: Int) -> ListCallDto): Li return results } -fun JsonElement.asMdMap(): Map { +fun JsonElement.asMdMap(): Map { return runCatching { - jsonObject.map { it.key to it.value.jsonPrimitive.contentOrNull.orEmpty() }.toMap() + MdUtil.jsonParser.decodeFromJsonElement>(jsonObject) }.getOrElse { emptyMap() } } diff --git a/app/src/main/java/exh/metadata/metadata/MangaDexSearchMetadata.kt b/app/src/main/java/exh/metadata/metadata/MangaDexSearchMetadata.kt index 5667ce5dd..cc5fab552 100644 --- a/app/src/main/java/exh/metadata/metadata/MangaDexSearchMetadata.kt +++ b/app/src/main/java/exh/metadata/metadata/MangaDexSearchMetadata.kt @@ -27,7 +27,7 @@ class MangaDexSearchMetadata : RaisedSearchMetadata() { var langFlag: String? = null var lastChapterNumber: Int? = null - // var rating: String? = null + var rating: Float? = null // var users: String? = null var anilistId: String? = null diff --git a/app/src/main/java/exh/ui/metadata/adapters/MangaDexDescriptionAdapter.kt b/app/src/main/java/exh/ui/metadata/adapters/MangaDexDescriptionAdapter.kt index 599e8edc1..0eb11b4e0 100644 --- a/app/src/main/java/exh/ui/metadata/adapters/MangaDexDescriptionAdapter.kt +++ b/app/src/main/java/exh/ui/metadata/adapters/MangaDexDescriptionAdapter.kt @@ -1,5 +1,6 @@ package exh.ui.metadata.adapters +import android.annotation.SuppressLint import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -10,9 +11,11 @@ import eu.kanade.tachiyomi.databinding.DescriptionAdapterMdBinding import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction import eu.kanade.tachiyomi.ui.manga.MangaController import eu.kanade.tachiyomi.util.system.copyToClipboard +import exh.metadata.MetadataUtil.getRatingString import exh.metadata.bindDrawable import exh.metadata.metadata.MangaDexSearchMetadata import exh.ui.metadata.MetadataViewController +import kotlin.math.round class MangaDexDescriptionAdapter( private val controller: MangaController @@ -39,12 +42,12 @@ class MangaDexDescriptionAdapter( if (meta == null || meta !is MangaDexSearchMetadata) return // todo - /*val ratingFloat = meta.rating?.toFloatOrNull() + val ratingFloat = meta.rating binding.ratingBar.rating = ratingFloat?.div(2F) ?: 0F @SuppressLint("SetTextI18n") - binding.rating.text = (round((meta.rating?.toFloatOrNull() ?: 0F) * 100.0) / 100.0).toString() + " - " + getRatingString(itemView.context, ratingFloat)*/ - binding.rating.isVisible = false - binding.ratingBar.isVisible = false + binding.rating.text = (round((ratingFloat ?: 0F) * 100.0) / 100.0).toString() + " - " + getRatingString(itemView.context, ratingFloat) + binding.rating.isVisible = ratingFloat != null + binding.ratingBar.isVisible = ratingFloat != null binding.moreInfo.bindDrawable(itemView.context, R.drawable.ic_info_24dp) diff --git a/app/src/main/res/layout/description_adapter_md.xml b/app/src/main/res/layout/description_adapter_md.xml index c5224cfb2..17b072ede 100644 --- a/app/src/main/res/layout/description_adapter_md.xml +++ b/app/src/main/res/layout/description_adapter_md.xml @@ -1,6 +1,7 @@ @@ -11,9 +12,11 @@ android:layout_marginStart="8dp" android:textAppearance="?attr/textAppearanceBodyMedium" android:textColor="?android:attr/textColorSecondary" + android:visibility="gone" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toEndOf="@+id/rating_bar" - app:layout_constraintTop_toTopOf="parent" /> + app:layout_constraintTop_toTopOf="parent" + tools:visibility="visible" /> + app:layout_constraintTop_toTopOf="parent" + tools:visibility="visible" />