From 950a2ac1b4bd54a491f736e90d5038471bf3faf8 Mon Sep 17 00:00:00 2001 From: Slowlife Date: Sun, 30 Apr 2023 06:27:04 +0700 Subject: [PATCH] MangaHub: Fix chapter pages not loading (#16229) * MangaHub: Fix chapter pages not loading * Make mangaSource nullable --- .../tachiyomi/multisrc/mangahub/MangaHub.kt | 91 ++++++++----------- .../multisrc/mangahub/MangaHubGenerator.kt | 2 +- .../multisrc/mangahub/MangaHubQueries.kt | 36 ++++++++ 3 files changed, 77 insertions(+), 52 deletions(-) create mode 100644 multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangahub/MangaHubQueries.kt diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangahub/MangaHub.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangahub/MangaHub.kt index 7da1d906a..84d5912b9 100644 --- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangahub/MangaHub.kt +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangahub/MangaHub.kt @@ -1,6 +1,7 @@ package eu.kanade.tachiyomi.multisrc.mangahub import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.network.interceptor.rateLimit import eu.kanade.tachiyomi.source.model.Filter import eu.kanade.tachiyomi.source.model.FilterList @@ -12,16 +13,18 @@ import eu.kanade.tachiyomi.source.online.ParsedHttpSource import eu.kanade.tachiyomi.util.asJsoup import kotlinx.serialization.decodeFromString import kotlinx.serialization.json.Json +import kotlinx.serialization.json.buildJsonObject +import kotlinx.serialization.json.put import okhttp3.Headers import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import okhttp3.Interceptor import okhttp3.OkHttpClient import okhttp3.Request +import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.Response import org.jsoup.nodes.Document import org.jsoup.nodes.Element import uy.kohesive.injekt.injectLazy -import java.io.IOException import java.text.ParseException import java.text.SimpleDateFormat import java.util.Calendar @@ -53,6 +56,9 @@ abstract class MangaHub( open val json: Json by injectLazy() + private var baseApiUrl = "https://api.mghubcdn.com" + private var baseCdnUrl = "https://imgx.mghubcdn.com" + private var userAgent: String? = null private var checkedUa = false @@ -271,62 +277,45 @@ abstract class MangaHub( } // pages - private fun findPageCount(urlTemplate: String, extension: String): Int { - var lowerBound = 1 - var upperBound = 500 + override fun pageListRequest(chapter: SChapter): Request { + val body = buildJsonObject { + put("query", PAGES_QUERY) + put( + "variables", + buildJsonObject { + val mangaSource = when (name) { + "MangaHub" -> "m01" + "MangaReader.site" -> "mr01" + "MangaPanda.onl" -> "mr02" + else -> null + } + val chapterUrl = chapter.url.split("/") - while (lowerBound <= upperBound) { - val midpoint = lowerBound + (upperBound - lowerBound) / 2 - val url = urlTemplate.replaceAfterLast("/", "$midpoint.$extension") - val request = Request.Builder() - .url(url) - .headers(headers) - .head() - .build() - val response = try { - client.newCall(request).execute() - } catch (e: IOException) { - throw Exception("Failed to fetch $url") - } - - if (response.code == 404) { - upperBound = midpoint - 1 - } else { - lowerBound = midpoint + 1 - } + put("mangaSource", mangaSource) + put("slug", chapterUrl[2]) + put("number", chapterUrl[3].substringAfter("-").toFloat()) + }, + ) } + .toString() + .toRequestBody() - return lowerBound - 1 - } - - override fun pageListParse(document: Document): List { - val pages = mutableListOf() - val urlTemplate = document.select("#mangareader img").attr("abs:src") - val extension = urlTemplate.substringAfterLast(".") - - // make some calls to check if the pages exist using findPageCount() - // increase or decreasing by using binary search algorithm - val maxPage = findPageCount(urlTemplate, extension) - - for (page in 1..maxPage) { - val url = urlTemplate.replaceAfterLast("/", "$page.$extension") - val pageObject = Page(page - 1, "", url) - pages.add(pageObject) - } - - return pages - } - - override fun imageUrlRequest(page: Page): Request { val newHeaders = headersBuilder() - .set("Accept", "image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8") - .set("Sec-Fetch-Dest", "image") - .set("Sec-Fetch-Mode", "no-cors") - .set("Sec-Fetch-Site", "cross-site") - .removeAll("Upgrade-Insecure-Requests") + .set("Content-Type", "application/json") + .set("Origin", baseUrl) .build() - return GET(page.url, newHeaders) + return POST("$baseApiUrl/graphql", newHeaders, body) + } + + override fun pageListParse(document: Document): List = throw UnsupportedOperationException("Not used") + override fun pageListParse(response: Response): List { + val pagesString = json.decodeFromString(response.body.string()).data.chapter.pages + val pages = json.decodeFromString(pagesString) + + return pages.i.mapIndexed { i, page -> + Page(i, "", "$baseCdnUrl/${pages.p}$page") + } } override fun imageUrlParse(document: Document): String = throw UnsupportedOperationException("Not used") diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangahub/MangaHubGenerator.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangahub/MangaHubGenerator.kt index c27b37781..db6e77b53 100644 --- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangahub/MangaHubGenerator.kt +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangahub/MangaHubGenerator.kt @@ -9,7 +9,7 @@ class MangaHubGenerator : ThemeSourceGenerator { override val themeClass = "MangaHub" - override val baseVersionCode: Int = 17 + override val baseVersionCode: Int = 18 override val sources = listOf( // SingleLang("1Manga.co", "https://1manga.co", "en", isNsfw = true, className = "OneMangaCo"), diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangahub/MangaHubQueries.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangahub/MangaHubQueries.kt new file mode 100644 index 000000000..717741b4b --- /dev/null +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangahub/MangaHubQueries.kt @@ -0,0 +1,36 @@ +package eu.kanade.tachiyomi.multisrc.mangahub + +import kotlinx.serialization.Serializable + +private fun buildQuery(queryAction: () -> String) = queryAction().replace("%", "$") + +val PAGES_QUERY = buildQuery { + """ + query(%mangaSource: MangaSource, %slug: String!, %number: Float!) { + chapter(x: %mangaSource, slug: %slug, number: %number) { + pages + } + } + """.trimIndent() +} + +@Serializable +data class ApiChapterPagesResponse( + val data: ApiChapterData, +) + +@Serializable +data class ApiChapterData( + val chapter: ApiChapter, +) + +@Serializable +data class ApiChapter( + val pages: String, +) + +@Serializable +data class ApiChapterPages( + val p: String, + val i: List, +)