Mangadex support manga rating

This commit is contained in:
Jobobby04 2021-12-27 16:18:02 -05:00
parent 77f5acf2dd
commit d7856fe351
10 changed files with 98 additions and 27 deletions

View File

@ -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<MangaDexSearchMetadata, Pair<MangaDto, List<String>>>,
MetadataSource<MangaDexSearchMetadata, Triple<MangaDto, List<String>, 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<MangaDto, List<String>>) {
apiMangaParser.parseIntoMetadata(metadata, input.first, input.second)
override suspend fun parseIntoMetadata(metadata: MangaDexSearchMetadata, input: Triple<MangaDto, List<String>, 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<Track, MangaDexSearchMetadata?> {
return mangaHandler.getTrackingInfo(track)

View File

@ -0,0 +1,18 @@
package exh.md.dto
import kotlinx.serialization.Serializable
@Serializable
data class StatisticsDto(
val statistics: Map<String, StatisticsMangaDto>
)
@Serializable
data class StatisticsMangaDto(
val rating: StatisticsMangaRatingDto
)
@Serializable
data class StatisticsMangaRatingDto(
val average: Double?
)

View File

@ -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<String>, sourceId: Long): MangaInfo {
fun parseToManga(
manga: MangaInfo,
sourceId: Long,
input: MangaDto,
simpleChapters: List<String>,
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<String>) {
fun parseIntoMetadata(
metadata: MangaDexSearchMetadata,
mangaDto: MangaDto,
simpleChapters: List<String>,
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<String>()?.let { links ->
links["al"]?.let { anilistId = it }
links["kt"]?.let { kitsuId = it }
links["mal"]?.let { myAnimeListId = it }

View File

@ -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<SManga> {

View File

@ -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

View File

@ -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"

View File

@ -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 <T> mdListCall(request: suspend (offset: Int) -> ListCallDto<T>): List<T> {
val results = mutableListOf<T>()
@ -20,8 +19,8 @@ suspend fun <T> mdListCall(request: suspend (offset: Int) -> ListCallDto<T>): Li
return results
}
fun JsonElement.asMdMap(): Map<String, String> {
fun <T> JsonElement.asMdMap(): Map<String, T> {
return runCatching {
jsonObject.map { it.key to it.value.jsonPrimitive.contentOrNull.orEmpty() }.toMap()
MdUtil.jsonParser.decodeFromJsonElement<Map<String, T>>(jsonObject)
}.getOrElse { emptyMap() }
}

View File

@ -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

View File

@ -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)

View File

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
@ -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" />
<me.zhanghai.android.materialratingbar.MaterialRatingBar
@ -25,9 +28,11 @@
android:focusable="false"
android:isIndicator="true"
android:numStars="5"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
app:layout_constraintTop_toTopOf="parent"
tools:visibility="visible" />
<Button
android:id="@+id/more_info"