Update mangadex to api 5.0.10

This commit is contained in:
Jobobby04 2021-05-22 14:38:47 -04:00
parent e37d4afce6
commit 20cbadb23d
7 changed files with 112 additions and 55 deletions

View File

@ -7,7 +7,6 @@ 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.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage import eu.kanade.tachiyomi.source.model.MangasPage
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
@ -267,29 +266,6 @@ class MangaDex(delegate: HttpSource, val context: Context) :
return similarHandler.getSimilar(manga) return similarHandler.getSimilar(manga)
} }
// todo remove when mangadex gets it cover api
override fun fetchSearchManga(
page: Int,
query: String,
filters: FilterList
): Observable<MangasPage> {
return super.fetchSearchManga(page, query, filters).doOnNext { mangaPage ->
mangaPage.mangas.forEach {
it.thumbnail_url = "https://coverapi.orell.dev/api/v1/mdaltimage/manga/${MdUtil.getMangaId(it.url)}/cover"
}
}
}
override fun fetchPopularManga(
page: Int
): Observable<MangasPage> {
return super.fetchPopularManga(page).doOnNext { mangaPage ->
mangaPage.mangas.forEach {
it.thumbnail_url = "https://coverapi.orell.dev/api/v1/mdaltimage/manga/${MdUtil.getMangaId(it.url)}/cover"
}
}
}
/*private fun importIdToMdId(query: String, fail: () -> Observable<MangasPage>): Observable<MangasPage> = /*private fun importIdToMdId(query: String, fail: () -> Observable<MangasPage>): Observable<MangasPage> =
when { when {
query.toIntOrNull() != null -> { query.toIntOrNull() != null -> {

View File

@ -9,6 +9,7 @@ import eu.kanade.tachiyomi.source.model.SManga
import exh.log.xLogE import exh.log.xLogE
import exh.md.handlers.serializers.AuthorResponseList import exh.md.handlers.serializers.AuthorResponseList
import exh.md.handlers.serializers.ChapterResponse import exh.md.handlers.serializers.ChapterResponse
import exh.md.handlers.serializers.CoverListResponse
import exh.md.handlers.serializers.MangaResponse import exh.md.handlers.serializers.MangaResponse
import exh.md.utils.MdUtil import exh.md.utils.MdUtil
import exh.metadata.metadata.MangaDexSearchMetadata import exh.metadata.metadata.MangaDexSearchMetadata
@ -72,13 +73,19 @@ class ApiMangaParser(val client: OkHttpClient, private val lang: String) {
mdUuid = networkApiManga.data.id mdUuid = networkApiManga.data.id
title = MdUtil.cleanString(networkManga.title[lang] ?: networkManga.title["en"]!!) title = MdUtil.cleanString(networkManga.title[lang] ?: networkManga.title["en"]!!)
altTitles = networkManga.altTitles.mapNotNull { it[lang] } altTitles = networkManga.altTitles.mapNotNull { it[lang] }
cover =
if (coverUrls.isNotEmpty()) { var coverUrl = MdUtil.formThumbUrl(networkApiManga.data.id)
coverUrls.last() val coverUrlId = networkApiManga.relationships.firstOrNull { it.type == "cover_art" }?.id
} else { if (coverUrlId != null) {
null runCatching {
// networkManga.mainCover val json = client.newCall(GET(MdUtil.coverUrl(networkApiManga.data.id, coverUrlId))).await()
.parseAs<CoverListResponse>(MdUtil.jsonParser)
json.results.firstOrNull()?.data?.attributes?.fileName?.let { fileName ->
coverUrl = "${MdUtil.cdnUrl}/covers/${networkApiManga.data.id}/$fileName"
}
} }
}
cover = coverUrl
description = MdUtil.cleanDescription(networkManga.description[lang] ?: networkManga.description["en"]!!) description = MdUtil.cleanDescription(networkManga.description[lang] ?: networkManga.description["en"]!!)
@ -130,16 +137,6 @@ class ApiMangaParser(val client: OkHttpClient, private val lang: String) {
links["ap"]?.let { animePlanetId = it } links["ap"]?.let { animePlanetId = it }
} }
if (kitsuId?.toIntOrNull() != null) {
cover = "https://media.kitsu.io/manga/poster_images/$kitsuId/large.jpg"
}
if (cover == null && !myAnimeListId.isNullOrEmpty()) {
cover = "https://coverapi.orell.dev/api/v1/mal/manga/$myAnimeListId/cover"
}
if (cover == null) {
cover = MdUtil.formThumbUrl(mdUuid.toString())
}
// val filteredChapters = filterChapterForChecking(networkApiManga) // val filteredChapters = filterChapterForChecking(networkApiManga)
val tempStatus = parseStatus(networkManga.status ?: "") val tempStatus = parseStatus(networkManga.status ?: "")

View File

@ -12,6 +12,7 @@ import eu.kanade.tachiyomi.source.model.MetadataMangasPage
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.model.toSManga import eu.kanade.tachiyomi.source.model.toSManga
import eu.kanade.tachiyomi.util.lang.withIOContext import eu.kanade.tachiyomi.util.lang.withIOContext
import exh.md.handlers.serializers.CoverListResponse
import exh.md.handlers.serializers.MangaListResponse import exh.md.handlers.serializers.MangaListResponse
import exh.md.handlers.serializers.MangaResponse import exh.md.handlers.serializers.MangaResponse
import exh.md.handlers.serializers.MangaStatusListResponse import exh.md.handlers.serializers.MangaStatusListResponse
@ -71,14 +72,27 @@ class FollowsHandler(
* Parse follows api to manga page * Parse follows api to manga page
* used when multiple follows * used when multiple follows
*/ */
private fun followsParseMangaPage(response: List<MangaResponse>, statuses: Map<String, String?>): List<Pair<SManga, MangaDexSearchMetadata>> { private suspend fun followsParseMangaPage(response: List<MangaResponse>, statuses: Map<String, String?>): List<Pair<SManga, MangaDexSearchMetadata>> {
val comparator = compareBy<Pair<SManga, MangaDexSearchMetadata>> { it.second.followStatus } val comparator = compareBy<Pair<SManga, MangaDexSearchMetadata>> { it.second.followStatus }
.thenBy { it.first.title } .thenBy { it.first.title }
return response.map { return response.map {
var coverUrl = MdUtil.formThumbUrl(it.data.id)
val coverUrlId = it.relationships.firstOrNull { it.type == "cover_art" }?.id
if (coverUrlId != null) {
runCatching {
val covers = client.newCall(GET(MdUtil.coverUrl(it.data.id, coverUrlId))).await()
.parseAs<CoverListResponse>()
covers.results.firstOrNull()?.data?.attributes?.fileName?.let { fileName ->
coverUrl = "${MdUtil.cdnUrl}/covers/${it.data.id}/$fileName"
}
}
}
MdUtil.createMangaEntry( MdUtil.createMangaEntry(
it, it,
lang lang,
coverUrl
).toSManga() to MangaDexSearchMetadata().apply { ).toSManga() to MangaDexSearchMetadata().apply {
followStatus = FollowStatus.fromDex(statuses[it.data.id]).int followStatus = FollowStatus.fromDex(statuses[it.data.id]).int
} }

View File

@ -2,9 +2,12 @@ package exh.md.handlers
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.network.await
import eu.kanade.tachiyomi.network.parseAs import eu.kanade.tachiyomi.network.parseAs
import eu.kanade.tachiyomi.source.model.MangasPage import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.toSManga import eu.kanade.tachiyomi.source.model.toSManga
import eu.kanade.tachiyomi.util.lang.runAsObservable
import exh.md.handlers.serializers.CoverListResponse
import exh.md.handlers.serializers.MangaListResponse import exh.md.handlers.serializers.MangaListResponse
import exh.md.utils.MdUtil import exh.md.utils.MdUtil
import okhttp3.CacheControl import okhttp3.CacheControl
@ -23,8 +26,10 @@ class PopularHandler(val client: OkHttpClient, private val headers: Headers, pri
fun fetchPopularManga(page: Int): Observable<MangasPage> { fun fetchPopularManga(page: Int): Observable<MangasPage> {
return client.newCall(popularMangaRequest(page)) return client.newCall(popularMangaRequest(page))
.asObservableSuccess() .asObservableSuccess()
.map { response -> .flatMap { response ->
popularMangaParse(response) runAsObservable({
popularMangaParse(response)
})
} }
} }
@ -39,10 +44,24 @@ class PopularHandler(val client: OkHttpClient, private val headers: Headers, pri
return GET(tempUrl.build().toString(), headers, CacheControl.FORCE_NETWORK) return GET(tempUrl.build().toString(), headers, CacheControl.FORCE_NETWORK)
} }
private fun popularMangaParse(response: Response): MangasPage { private suspend fun popularMangaParse(response: Response): MangasPage {
val mlResponse = response.parseAs<MangaListResponse>(MdUtil.jsonParser) val mlResponse = response.parseAs<MangaListResponse>(MdUtil.jsonParser)
val hasMoreResults = mlResponse.limit + mlResponse.offset < mlResponse.total val hasMoreResults = mlResponse.limit + mlResponse.offset < mlResponse.total
val mangaList = mlResponse.results.map { MdUtil.createMangaEntry(it, lang).toSManga() }
val mangaList = mlResponse.results.map {
var coverUrl = MdUtil.formThumbUrl(it.data.id)
val coverUrlId = it.relationships.firstOrNull { it.type == "cover_art" }?.id
if (coverUrlId != null) {
runCatching {
val covers = client.newCall(GET(MdUtil.coverUrl(it.data.id, coverUrlId))).await()
.parseAs<CoverListResponse>()
covers.results.firstOrNull()?.data?.attributes?.fileName?.let { fileName ->
coverUrl = "${MdUtil.cdnUrl}/covers/${it.data.id}/$fileName"
}
}
}
MdUtil.createMangaEntry(it, lang, coverUrl).toSManga()
}
return MangasPage(mangaList, hasMoreResults) return MangasPage(mangaList, hasMoreResults)
} }
} }

View File

@ -2,11 +2,13 @@ package exh.md.handlers
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.network.await
import eu.kanade.tachiyomi.network.parseAs import eu.kanade.tachiyomi.network.parseAs
import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.toSManga import eu.kanade.tachiyomi.source.model.toSManga
import eu.kanade.tachiyomi.util.lang.runAsObservable import eu.kanade.tachiyomi.util.lang.runAsObservable
import exh.md.handlers.serializers.CoverListResponse
import exh.md.handlers.serializers.MangaListResponse import exh.md.handlers.serializers.MangaListResponse
import exh.md.handlers.serializers.MangaResponse import exh.md.handlers.serializers.MangaResponse
import exh.md.utils.MdUtil import exh.md.utils.MdUtil
@ -28,24 +30,52 @@ class SearchHandler(val client: OkHttpClient, private val headers: Headers, val
.flatMap { response -> .flatMap { response ->
runAsObservable({ runAsObservable({
val mangaResponse = response.parseAs<MangaResponse>(MdUtil.jsonParser) val mangaResponse = response.parseAs<MangaResponse>(MdUtil.jsonParser)
var coverUrl = MdUtil.formThumbUrl(mangaResponse.data.id)
val coverUrlId = mangaResponse.relationships.firstOrNull { it.type == "cover_art" }?.id
if (coverUrlId != null) {
runCatching {
val covers = client.newCall(GET(MdUtil.coverUrl(mangaResponse.data.id, coverUrlId))).await()
.parseAs<CoverListResponse>()
covers.results.firstOrNull()?.data?.attributes?.fileName?.let { fileName ->
coverUrl = "${MdUtil.cdnUrl}/covers/${mangaResponse.data.id}/$fileName"
}
}
}
val details = apiMangaParser val details = apiMangaParser
.parseToManga(MdUtil.createMangaEntry(mangaResponse, lang), response, emptyList(), sourceId).toSManga() .parseToManga(MdUtil.createMangaEntry(mangaResponse, lang, coverUrl), response, emptyList(), sourceId).toSManga()
MangasPage(listOf(details), false) MangasPage(listOf(details), false)
}) })
} }
} else { } else {
client.newCall(searchMangaRequest(page, query, filters)) client.newCall(searchMangaRequest(page, query, filters))
.asObservableSuccess() .asObservableSuccess()
.map { response -> .flatMap { response ->
searchMangaParse(response) runAsObservable({
searchMangaParse(response)
})
} }
} }
} }
private fun searchMangaParse(response: Response): MangasPage { private suspend fun searchMangaParse(response: Response): MangasPage {
val mlResponse = response.parseAs<MangaListResponse>(MdUtil.jsonParser) val mlResponse = response.parseAs<MangaListResponse>(MdUtil.jsonParser)
val hasMoreResults = mlResponse.limit + mlResponse.offset < mlResponse.total val hasMoreResults = mlResponse.limit + mlResponse.offset < mlResponse.total
val mangaList = mlResponse.results.map { MdUtil.createMangaEntry(it, lang).toSManga() } val mangaList = mlResponse.results.map {
var coverUrl = MdUtil.formThumbUrl(it.data.id)
val coverUrlId = it.relationships.firstOrNull { it.type == "cover_art" }?.id
if (coverUrlId != null) {
runCatching {
val covers = client.newCall(GET(MdUtil.coverUrl(it.data.id, coverUrlId))).await()
.parseAs<CoverListResponse>()
covers.results.firstOrNull()?.data?.attributes?.fileName?.let { fileName ->
coverUrl = "${MdUtil.cdnUrl}/covers/${it.data.id}/$fileName"
}
}
}
MdUtil.createMangaEntry(it, lang, coverUrl).toSManga()
}
return MangasPage(mangaList, hasMoreResults) return MangasPage(mangaList, hasMoreResults)
} }

View File

@ -89,3 +89,23 @@ data class MangaStatusResponse(
data class MangaStatusListResponse( data class MangaStatusListResponse(
val statuses: Map<String, String?> val statuses: Map<String, String?>
) )
@Serializable
data class CoverListResponse(
val results: List<CoverResponse>,
)
@Serializable
data class CoverResponse(
val data: Cover,
)
@Serializable
data class Cover(
val attributes: CoverAttributes,
)
@Serializable
data class CoverAttributes(
val fileName: String,
)

View File

@ -62,18 +62,19 @@ class MdUtil {
return "$mangaUrl/$id/feed".toHttpUrl().newBuilder().apply { return "$mangaUrl/$id/feed".toHttpUrl().newBuilder().apply {
addQueryParameter("limit", "500") addQueryParameter("limit", "500")
addQueryParameter("offset", offset.toString()) addQueryParameter("offset", offset.toString())
addQueryParameter("locales[]", language) addQueryParameter("translatedLanguage[]", language)
addQueryParameter("order[volume]", "desc") addQueryParameter("order[volume]", "desc")
addQueryParameter("order[chapter]", "desc") addQueryParameter("order[chapter]", "desc")
}.build().toString() }.build().toString()
} }
fun coverUrl(mangaId: String, coverId: String) = "$apiUrl/cover/?manga[]=$mangaId&ids[]=$coverId"
const val similarCache = "https://raw.githubusercontent.com/goldbattle/MangadexRecomendations/master/output/api/" const val similarCache = "https://raw.githubusercontent.com/goldbattle/MangadexRecomendations/master/output/api/"
const val similarCacheCdn = "https://cdn.statically.io/gh/goldbattle/MangadexRecomendations/master/output/api/" const val similarCacheCdn = "https://cdn.statically.io/gh/goldbattle/MangadexRecomendations/master/output/api/"
const val similarBaseApi = "https://api.similarmanga.com/similar/" const val similarBaseApi = "https://api.similarmanga.com/similar/"
const val groupSearchUrl = "$baseUrl/groups/0/1/" const val groupSearchUrl = "$baseUrl/groups/0/1/"
const val apiCovers = "/covers"
const val reportUrl = "https://api.mangadex.network/report" const val reportUrl = "https://api.mangadex.network/report"
const val mdAtHomeTokenLifespan = 10 * 60 * 1000 const val mdAtHomeTokenLifespan = 10 * 60 * 1000
@ -300,12 +301,12 @@ class MdUtil {
fun parseDate(dateAsString: String): Long = fun parseDate(dateAsString: String): Long =
dateFormatter.parse(dateAsString)?.time ?: 0 dateFormatter.parse(dateAsString)?.time ?: 0
fun createMangaEntry(json: MangaResponse, lang: String): MangaInfo { fun createMangaEntry(json: MangaResponse, lang: String, coverUrl: String): MangaInfo {
val key = buildMangaUrl(json.data.id) val key = buildMangaUrl(json.data.id)
return MangaInfo( return MangaInfo(
key = key, key = key,
title = cleanString(json.data.attributes.title[lang] ?: json.data.attributes.title["en"]!!), title = cleanString(json.data.attributes.title[lang] ?: json.data.attributes.title["en"]!!),
cover = formThumbUrl(key) cover = coverUrl
) )
} }