From 20cbadb23d9ea9ef56b0ffc37ba6ed3b44e6c856 Mon Sep 17 00:00:00 2001 From: Jobobby04 Date: Sat, 22 May 2021 14:38:47 -0400 Subject: [PATCH] Update mangadex to api 5.0.10 --- .../tachiyomi/source/online/all/MangaDex.kt | 24 ----------- .../java/exh/md/handlers/ApiMangaParser.kt | 29 ++++++-------- .../java/exh/md/handlers/FollowsHandler.kt | 18 ++++++++- .../java/exh/md/handlers/PopularHandler.kt | 27 +++++++++++-- .../java/exh/md/handlers/SearchHandler.kt | 40 ++++++++++++++++--- .../handlers/serializers/MangaSerializer.kt | 20 ++++++++++ app/src/main/java/exh/md/utils/MdUtil.kt | 9 +++-- 7 files changed, 112 insertions(+), 55 deletions(-) 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 b8e23d598..b38f8d142 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 @@ -7,7 +7,6 @@ import eu.kanade.tachiyomi.data.track.TrackManager import eu.kanade.tachiyomi.data.track.mdlist.MdList import eu.kanade.tachiyomi.network.GET 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.Page import eu.kanade.tachiyomi.source.model.SChapter @@ -267,29 +266,6 @@ class MangaDex(delegate: HttpSource, val context: Context) : return similarHandler.getSimilar(manga) } - // todo remove when mangadex gets it cover api - override fun fetchSearchManga( - page: Int, - query: String, - filters: FilterList - ): Observable { - 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 { - 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): Observable = when { query.toIntOrNull() != null -> { diff --git a/app/src/main/java/exh/md/handlers/ApiMangaParser.kt b/app/src/main/java/exh/md/handlers/ApiMangaParser.kt index 523c3c1af..322add116 100644 --- a/app/src/main/java/exh/md/handlers/ApiMangaParser.kt +++ b/app/src/main/java/exh/md/handlers/ApiMangaParser.kt @@ -9,6 +9,7 @@ import eu.kanade.tachiyomi.source.model.SManga import exh.log.xLogE import exh.md.handlers.serializers.AuthorResponseList import exh.md.handlers.serializers.ChapterResponse +import exh.md.handlers.serializers.CoverListResponse import exh.md.handlers.serializers.MangaResponse import exh.md.utils.MdUtil import exh.metadata.metadata.MangaDexSearchMetadata @@ -72,13 +73,19 @@ class ApiMangaParser(val client: OkHttpClient, private val lang: String) { mdUuid = networkApiManga.data.id title = MdUtil.cleanString(networkManga.title[lang] ?: networkManga.title["en"]!!) altTitles = networkManga.altTitles.mapNotNull { it[lang] } - cover = - if (coverUrls.isNotEmpty()) { - coverUrls.last() - } else { - null - // networkManga.mainCover + + var coverUrl = MdUtil.formThumbUrl(networkApiManga.data.id) + val coverUrlId = networkApiManga.relationships.firstOrNull { it.type == "cover_art" }?.id + if (coverUrlId != null) { + runCatching { + val json = client.newCall(GET(MdUtil.coverUrl(networkApiManga.data.id, coverUrlId))).await() + .parseAs(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"]!!) @@ -130,16 +137,6 @@ class ApiMangaParser(val client: OkHttpClient, private val lang: String) { 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 tempStatus = parseStatus(networkManga.status ?: "") diff --git a/app/src/main/java/exh/md/handlers/FollowsHandler.kt b/app/src/main/java/exh/md/handlers/FollowsHandler.kt index 9210c6e22..362309f8f 100644 --- a/app/src/main/java/exh/md/handlers/FollowsHandler.kt +++ b/app/src/main/java/exh/md/handlers/FollowsHandler.kt @@ -12,6 +12,7 @@ import eu.kanade.tachiyomi.source.model.MetadataMangasPage import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.toSManga import eu.kanade.tachiyomi.util.lang.withIOContext +import exh.md.handlers.serializers.CoverListResponse import exh.md.handlers.serializers.MangaListResponse import exh.md.handlers.serializers.MangaResponse import exh.md.handlers.serializers.MangaStatusListResponse @@ -71,14 +72,27 @@ class FollowsHandler( * Parse follows api to manga page * used when multiple follows */ - private fun followsParseMangaPage(response: List, statuses: Map): List> { + private suspend fun followsParseMangaPage(response: List, statuses: Map): List> { val comparator = compareBy> { it.second.followStatus } .thenBy { it.first.title } 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() + covers.results.firstOrNull()?.data?.attributes?.fileName?.let { fileName -> + coverUrl = "${MdUtil.cdnUrl}/covers/${it.data.id}/$fileName" + } + } + } + MdUtil.createMangaEntry( it, - lang + lang, + coverUrl ).toSManga() to MangaDexSearchMetadata().apply { followStatus = FollowStatus.fromDex(statuses[it.data.id]).int } diff --git a/app/src/main/java/exh/md/handlers/PopularHandler.kt b/app/src/main/java/exh/md/handlers/PopularHandler.kt index e1173eb57..3ad0e4066 100644 --- a/app/src/main/java/exh/md/handlers/PopularHandler.kt +++ b/app/src/main/java/exh/md/handlers/PopularHandler.kt @@ -2,9 +2,12 @@ package exh.md.handlers import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.asObservableSuccess +import eu.kanade.tachiyomi.network.await import eu.kanade.tachiyomi.network.parseAs import eu.kanade.tachiyomi.source.model.MangasPage 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.utils.MdUtil import okhttp3.CacheControl @@ -23,8 +26,10 @@ class PopularHandler(val client: OkHttpClient, private val headers: Headers, pri fun fetchPopularManga(page: Int): Observable { return client.newCall(popularMangaRequest(page)) .asObservableSuccess() - .map { response -> - popularMangaParse(response) + .flatMap { 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) } - private fun popularMangaParse(response: Response): MangasPage { + private suspend fun popularMangaParse(response: Response): MangasPage { val mlResponse = response.parseAs(MdUtil.jsonParser) 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() + 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) } } diff --git a/app/src/main/java/exh/md/handlers/SearchHandler.kt b/app/src/main/java/exh/md/handlers/SearchHandler.kt index 7f4e27daa..f4fb2add4 100644 --- a/app/src/main/java/exh/md/handlers/SearchHandler.kt +++ b/app/src/main/java/exh/md/handlers/SearchHandler.kt @@ -2,11 +2,13 @@ package exh.md.handlers import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.asObservableSuccess +import eu.kanade.tachiyomi.network.await import eu.kanade.tachiyomi.network.parseAs import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.MangasPage 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.MangaResponse import exh.md.utils.MdUtil @@ -28,24 +30,52 @@ class SearchHandler(val client: OkHttpClient, private val headers: Headers, val .flatMap { response -> runAsObservable({ val mangaResponse = response.parseAs(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() + covers.results.firstOrNull()?.data?.attributes?.fileName?.let { fileName -> + coverUrl = "${MdUtil.cdnUrl}/covers/${mangaResponse.data.id}/$fileName" + } + } + } + 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) }) } } else { client.newCall(searchMangaRequest(page, query, filters)) .asObservableSuccess() - .map { response -> - searchMangaParse(response) + .flatMap { response -> + runAsObservable({ + searchMangaParse(response) + }) } } } - private fun searchMangaParse(response: Response): MangasPage { + private suspend fun searchMangaParse(response: Response): MangasPage { val mlResponse = response.parseAs(MdUtil.jsonParser) 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() + 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) } diff --git a/app/src/main/java/exh/md/handlers/serializers/MangaSerializer.kt b/app/src/main/java/exh/md/handlers/serializers/MangaSerializer.kt index 284ec37ac..5aae4653a 100644 --- a/app/src/main/java/exh/md/handlers/serializers/MangaSerializer.kt +++ b/app/src/main/java/exh/md/handlers/serializers/MangaSerializer.kt @@ -89,3 +89,23 @@ data class MangaStatusResponse( data class MangaStatusListResponse( val statuses: Map ) + +@Serializable +data class CoverListResponse( + val results: List, +) + +@Serializable +data class CoverResponse( + val data: Cover, +) + +@Serializable +data class Cover( + val attributes: CoverAttributes, +) + +@Serializable +data class CoverAttributes( + val fileName: String, +) diff --git a/app/src/main/java/exh/md/utils/MdUtil.kt b/app/src/main/java/exh/md/utils/MdUtil.kt index 1273b5627..07feaa184 100644 --- a/app/src/main/java/exh/md/utils/MdUtil.kt +++ b/app/src/main/java/exh/md/utils/MdUtil.kt @@ -62,18 +62,19 @@ class MdUtil { return "$mangaUrl/$id/feed".toHttpUrl().newBuilder().apply { addQueryParameter("limit", "500") addQueryParameter("offset", offset.toString()) - addQueryParameter("locales[]", language) + addQueryParameter("translatedLanguage[]", language) addQueryParameter("order[volume]", "desc") addQueryParameter("order[chapter]", "desc") }.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 similarCacheCdn = "https://cdn.statically.io/gh/goldbattle/MangadexRecomendations/master/output/api/" const val similarBaseApi = "https://api.similarmanga.com/similar/" const val groupSearchUrl = "$baseUrl/groups/0/1/" - const val apiCovers = "/covers" const val reportUrl = "https://api.mangadex.network/report" const val mdAtHomeTokenLifespan = 10 * 60 * 1000 @@ -300,12 +301,12 @@ class MdUtil { fun parseDate(dateAsString: String): Long = 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) return MangaInfo( key = key, title = cleanString(json.data.attributes.title[lang] ?: json.data.attributes.title["en"]!!), - cover = formThumbUrl(key) + cover = coverUrl ) }