Show Mangadex relations in Mangadex similar

This commit is contained in:
Jobobby04 2021-12-27 16:12:09 -05:00
parent 87d9512b1f
commit 77f5acf2dd
11 changed files with 156 additions and 8 deletions

View File

@ -9,6 +9,7 @@ import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.data.track.mdlist.MdList import eu.kanade.tachiyomi.data.track.mdlist.MdList
import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.source.model.MangasPage import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.MetadataMangasPage
import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
@ -284,10 +285,14 @@ class MangaDex(delegate: HttpSource, val context: Context) :
return mangaHandler.fetchRandomMangaId() return mangaHandler.fetchRandomMangaId()
} }
suspend fun getMangaSimilar(manga: MangaInfo): MangasPage { suspend fun getMangaSimilar(manga: MangaInfo): MetadataMangasPage {
return similarHandler.getSimilar(manga) return similarHandler.getSimilar(manga)
} }
suspend fun getMangaRelated(manga: MangaInfo): MetadataMangasPage {
return similarHandler.getRelated(manga)
}
companion object { companion object {
private const val dataSaverPref = "dataSaverV5" private const val dataSaverPref = "dataSaverV5"

View File

@ -61,6 +61,10 @@ class SourceComfortableGridHolder(private val view: View, private val adapter: F
binding.badges.localText.text = itemView.context.resources.getStringArray(R.array.md_follows_options).asList()[it] binding.badges.localText.text = itemView.context.resources.getStringArray(R.array.md_follows_options).asList()[it]
binding.badges.localText.isVisible = true binding.badges.localText.isVisible = true
} }
metadata.relation?.let {
binding.badges.localText.setText(it.resId)
binding.badges.localText.isVisible = true
}
} }
} }
// SY <-- // SY <--

View File

@ -58,6 +58,10 @@ open class SourceCompactGridHolder(private val view: View, private val adapter:
binding.badges.localText.text = itemView.context.resources.getStringArray(R.array.md_follows_options).asList()[it] binding.badges.localText.text = itemView.context.resources.getStringArray(R.array.md_follows_options).asList()[it]
binding.badges.localText.isVisible = true binding.badges.localText.isVisible = true
} }
metadata.relation?.let {
binding.badges.localText.setText(it.resId)
binding.badges.localText.isVisible = true
}
} }
} }
// SY <-- // SY <--

View File

@ -58,6 +58,10 @@ class SourceListHolder(private val view: View, adapter: FlexibleAdapter<*>) :
binding.localText.text = itemView.context.resources.getStringArray(R.array.md_follows_options).asList()[it] binding.localText.text = itemView.context.resources.getStringArray(R.array.md_follows_options).asList()[it]
binding.localText.isVisible = true binding.localText.isVisible = true
} }
metadata.relation?.let {
binding.localText.setText(it.resId)
binding.localText.isVisible = true
}
} }
} }
// SY <-- // SY <--

View File

@ -18,3 +18,25 @@ data class SimilarMangaMatchListDto(
val contentRating: String, val contentRating: String,
val score: Double, val score: Double,
) )
@Serializable
data class RelationListDto(
val response: String,
val data: List<RelationDto>,
)
@Serializable
data class RelationDto(
val attributes: RelationAttributesDto,
val relationships: List<RelationMangaDto>,
)
@Serializable
data class RelationMangaDto(
val id: String
)
@Serializable
data class RelationAttributesDto(
val relation: String,
)

View File

@ -1,11 +1,15 @@
package exh.md.handlers package exh.md.handlers
import eu.kanade.tachiyomi.source.model.MangasPage import eu.kanade.tachiyomi.source.model.MetadataMangasPage
import eu.kanade.tachiyomi.source.model.toSManga import eu.kanade.tachiyomi.source.model.toSManga
import eu.kanade.tachiyomi.util.lang.withIOContext
import exh.md.dto.RelationListDto
import exh.md.dto.SimilarMangaDto import exh.md.dto.SimilarMangaDto
import exh.md.service.MangaDexService import exh.md.service.MangaDexService
import exh.md.service.SimilarService import exh.md.service.SimilarService
import exh.md.utils.MangaDexRelation
import exh.md.utils.MdUtil import exh.md.utils.MdUtil
import exh.metadata.metadata.MangaDexSearchMetadata
import tachiyomi.source.model.MangaInfo import tachiyomi.source.model.MangaInfo
class SimilarHandler( class SimilarHandler(
@ -14,14 +18,14 @@ class SimilarHandler(
private val similarService: SimilarService private val similarService: SimilarService
) { ) {
suspend fun getSimilar(manga: MangaInfo): MangasPage { suspend fun getSimilar(manga: MangaInfo): MetadataMangasPage {
val similarDto = similarService.getSimilarManga(MdUtil.getMangaId(manga.key)) val similarDto = withIOContext { similarService.getSimilarManga(MdUtil.getMangaId(manga.key)) }
return similarDtoToMangaListPage(similarDto) return similarDtoToMangaListPage(similarDto)
} }
private suspend fun similarDtoToMangaListPage( private suspend fun similarDtoToMangaListPage(
similarMangaDto: SimilarMangaDto, similarMangaDto: SimilarMangaDto,
): MangasPage { ): MetadataMangasPage {
val ids = similarMangaDto.matches.map { val ids = similarMangaDto.matches.map {
it.id it.id
} }
@ -30,6 +34,34 @@ class SimilarHandler(
MdUtil.createMangaEntry(it, lang).toSManga() MdUtil.createMangaEntry(it, lang).toSManga()
} }
return MangasPage(mangaList, false) return MetadataMangasPage(mangaList, false, List(mangaList.size) { MangaDexSearchMetadata().also { it.relation = MangaDexRelation.SIMILAR } })
}
suspend fun getRelated(manga: MangaInfo): MetadataMangasPage {
val relatedListDto = withIOContext { service.relatedManga(MdUtil.getMangaId(manga.key)) }
return relatedDtoToMangaListPage(relatedListDto)
}
private suspend fun relatedDtoToMangaListPage(
relatedListDto: RelationListDto,
): MetadataMangasPage {
val ids = relatedListDto.data
.mapNotNull { it.relationships.firstOrNull() }
.map { it.id }
val mangaList = service.viewMangas(ids).data.map {
MdUtil.createMangaEntry(it, lang).toSManga()
}
return MetadataMangasPage(
mangas = mangaList,
hasNextPage = false,
mangasMetadata = mangaList.map { manga ->
MangaDexSearchMetadata().also {
it.relation = relatedListDto.data.firstOrNull { it.relationships.any { it.id == MdUtil.getMangaId(manga.url) } }
?.attributes?.relation?.let(MangaDexRelation::fromDex)
}
}
)
} }
} }

View File

@ -11,6 +11,7 @@ import exh.md.dto.ChapterDto
import exh.md.dto.ChapterListDto import exh.md.dto.ChapterListDto
import exh.md.dto.MangaDto import exh.md.dto.MangaDto
import exh.md.dto.MangaListDto import exh.md.dto.MangaListDto
import exh.md.dto.RelationListDto
import exh.md.dto.ResultDto import exh.md.dto.ResultDto
import exh.md.utils.MdApi import exh.md.utils.MdApi
import exh.md.utils.MdConstants import exh.md.utils.MdConstants
@ -145,6 +146,21 @@ class MangaDexService(
): AtHomeDto { ): AtHomeDto {
return client.newCall(GET(atHomeRequestUrl, headers, CacheControl.FORCE_NETWORK)) return client.newCall(GET(atHomeRequestUrl, headers, CacheControl.FORCE_NETWORK))
.await() .await()
.parseAs() .parseAs(MdUtil.jsonParser)
}
suspend fun relatedManga(id: String): RelationListDto {
return client.newCall(
GET(
MdApi.manga.toHttpUrl().newBuilder()
.apply {
addPathSegment(id)
addPathSegment("relation")
}
.build()
.toString(),
cache = CacheControl.FORCE_NETWORK
)
).await().parseAs(MdUtil.jsonParser)
} }
} }

View File

@ -2,9 +2,12 @@ package exh.md.similar
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.database.models.toMangaInfo import eu.kanade.tachiyomi.data.database.models.toMangaInfo
import eu.kanade.tachiyomi.source.model.MetadataMangasPage
import eu.kanade.tachiyomi.source.online.all.MangaDex import eu.kanade.tachiyomi.source.online.all.MangaDex
import eu.kanade.tachiyomi.ui.browse.source.browse.NoResultsException import eu.kanade.tachiyomi.ui.browse.source.browse.NoResultsException
import eu.kanade.tachiyomi.ui.browse.source.browse.Pager import eu.kanade.tachiyomi.ui.browse.source.browse.Pager
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
/** /**
* MangaDexSimilarPager inherited from the general Pager. * MangaDexSimilarPager inherited from the general Pager.
@ -12,7 +15,18 @@ import eu.kanade.tachiyomi.ui.browse.source.browse.Pager
class MangaDexSimilarPager(val manga: Manga, val source: MangaDex) : Pager() { class MangaDexSimilarPager(val manga: Manga, val source: MangaDex) : Pager() {
override suspend fun requestNextPage() { override suspend fun requestNextPage() {
val mangasPage = source.getMangaSimilar(manga.toMangaInfo()) val mangasPage = coroutineScope {
val similarPageDef = async { source.getMangaSimilar(manga.toMangaInfo()) }
val relatedPageDef = async { source.getMangaRelated(manga.toMangaInfo()) }
val similarPage = similarPageDef.await()
val relatedPage = relatedPageDef.await()
MetadataMangasPage(
relatedPage.mangas + similarPage.mangas,
false,
relatedPage.mangasMetadata + similarPage.mangasMetadata
)
}
if (mangasPage.mangas.isNotEmpty()) { if (mangasPage.mangas.isNotEmpty()) {
onPageReceived(mangasPage) onPageReceived(mangasPage)

View File

@ -0,0 +1,27 @@
package exh.md.utils
import androidx.annotation.StringRes
import eu.kanade.tachiyomi.R
enum class MangaDexRelation(@StringRes val resId: Int, val mdString: String?) {
SIMILAR(R.string.relation_similar, null),
MONOCHROME(R.string.relation_monochrome, "monochrome"),
MAIN_STORY(R.string.relation_main_story, "main_story"),
ADAPTED_FROM(R.string.relation_adapted_from, "adapted_from"),
BASED_ON(R.string.relation_based_on, "based_on"),
PREQUEL(R.string.relation_prequel, "prequel"),
SIDE_STORY(R.string.relation_side_story, "side_story"),
DOUJINSHI(R.string.relation_doujinshi, "doujinshi"),
SAME_FRANCHISE(R.string.relation_same_franchise, "same_franchise"),
SHARED_UNIVERSE(R.string.relation_shared_universe, "shared_universe"),
SEQUEL(R.string.relation_sequel, "sequel"),
SPIN_OFF(R.string.relation_spin_off, "spin_off"),
ALTERNATE_STORY(R.string.relation_alternate_story, "alternate_story"),
PRESERIALIZATION(R.string.relation_preserialization, "preserialization"),
COLORED(R.string.relation_colored, "colored"),
SERIALIZATION(R.string.relation_serialization, "serialization");
companion object {
fun fromDex(mdString: String) = values().find { it.mdString == mdString }
}
}

View File

@ -2,6 +2,7 @@ package exh.metadata.metadata
import android.content.Context import android.content.Context
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import exh.md.utils.MangaDexRelation
import exh.md.utils.MdUtil import exh.md.utils.MdUtil
import exh.metadata.metadata.base.RaisedSearchMetadata import exh.metadata.metadata.base.RaisedSearchMetadata
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@ -40,6 +41,7 @@ class MangaDexSearchMetadata : RaisedSearchMetadata() {
// var missing_chapters: String? = null // var missing_chapters: String? = null
var followStatus: Int? = null var followStatus: Int? = null
var relation: MangaDexRelation? = null
// var maxChapterNumber: Int? = null // var maxChapterNumber: Int? = null

View File

@ -669,6 +669,24 @@
<!-- Similar --> <!-- Similar -->
<string name="similar">Similar to %1$s</string> <string name="similar">Similar to %1$s</string>
<string name="similar_no_results">No Similar Manga found</string> <string name="similar_no_results">No Similar Manga found</string>
<!-- Mangadex relations-->
<string name="relation_similar">Similar</string>
<string name="relation_monochrome">Monochrome</string>
<string name="relation_main_story">Main story</string>
<string name="relation_adapted_from">Adapted from</string>
<string name="relation_based_on">Based on</string>
<string name="relation_prequel">Prequel</string>
<string name="relation_side_story">Side story</string>
<string name="relation_doujinshi">Doujinshi</string>
<string name="relation_same_franchise">Same franchise</string>
<string name="relation_shared_universe">Shared universe</string>
<string name="relation_sequel">Sequel</string>
<string name="relation_spin_off">Spin-off</string>
<string name="relation_alternate_story">Alternate story</string>
<string name="relation_preserialization">Pre-serialization</string>
<string name="relation_colored">Colored</string>
<string name="relation_serialization">Serialization</string>
<!-- Humanize time --> <!-- Humanize time -->
<plurals name="humanize_year"> <plurals name="humanize_year">