MangaHub: Fix chapter pages not loading (#16229)

* MangaHub: Fix chapter pages not loading

* Make mangaSource nullable
This commit is contained in:
Slowlife 2023-04-30 06:27:04 +07:00 committed by GitHub
parent 406e2ddfb5
commit 950a2ac1b4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 77 additions and 52 deletions

View File

@ -1,6 +1,7 @@
package eu.kanade.tachiyomi.multisrc.mangahub package eu.kanade.tachiyomi.multisrc.mangahub
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.network.interceptor.rateLimit import eu.kanade.tachiyomi.network.interceptor.rateLimit
import eu.kanade.tachiyomi.source.model.Filter import eu.kanade.tachiyomi.source.model.Filter
import eu.kanade.tachiyomi.source.model.FilterList 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 eu.kanade.tachiyomi.util.asJsoup
import kotlinx.serialization.decodeFromString import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.put
import okhttp3.Headers import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.Interceptor import okhttp3.Interceptor
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response import okhttp3.Response
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.io.IOException
import java.text.ParseException import java.text.ParseException
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Calendar import java.util.Calendar
@ -53,6 +56,9 @@ abstract class MangaHub(
open val json: Json by injectLazy() 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 userAgent: String? = null
private var checkedUa = false private var checkedUa = false
@ -271,62 +277,45 @@ abstract class MangaHub(
} }
// pages // pages
private fun findPageCount(urlTemplate: String, extension: String): Int { override fun pageListRequest(chapter: SChapter): Request {
var lowerBound = 1 val body = buildJsonObject {
var upperBound = 500 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) { put("mangaSource", mangaSource)
val midpoint = lowerBound + (upperBound - lowerBound) / 2 put("slug", chapterUrl[2])
val url = urlTemplate.replaceAfterLast("/", "$midpoint.$extension") put("number", chapterUrl[3].substringAfter("-").toFloat())
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
}
} }
.toString()
.toRequestBody()
return lowerBound - 1
}
override fun pageListParse(document: Document): List<Page> {
val pages = mutableListOf<Page>()
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() val newHeaders = headersBuilder()
.set("Accept", "image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8") .set("Content-Type", "application/json")
.set("Sec-Fetch-Dest", "image") .set("Origin", baseUrl)
.set("Sec-Fetch-Mode", "no-cors")
.set("Sec-Fetch-Site", "cross-site")
.removeAll("Upgrade-Insecure-Requests")
.build() .build()
return GET(page.url, newHeaders) return POST("$baseApiUrl/graphql", newHeaders, body)
}
override fun pageListParse(document: Document): List<Page> = throw UnsupportedOperationException("Not used")
override fun pageListParse(response: Response): List<Page> {
val pagesString = json.decodeFromString<ApiChapterPagesResponse>(response.body.string()).data.chapter.pages
val pages = json.decodeFromString<ApiChapterPages>(pagesString)
return pages.i.mapIndexed { i, page ->
Page(i, "", "$baseCdnUrl/${pages.p}$page")
}
} }
override fun imageUrlParse(document: Document): String = throw UnsupportedOperationException("Not used") override fun imageUrlParse(document: Document): String = throw UnsupportedOperationException("Not used")

View File

@ -9,7 +9,7 @@ class MangaHubGenerator : ThemeSourceGenerator {
override val themeClass = "MangaHub" override val themeClass = "MangaHub"
override val baseVersionCode: Int = 17 override val baseVersionCode: Int = 18
override val sources = listOf( override val sources = listOf(
// SingleLang("1Manga.co", "https://1manga.co", "en", isNsfw = true, className = "OneMangaCo"), // SingleLang("1Manga.co", "https://1manga.co", "en", isNsfw = true, className = "OneMangaCo"),

View File

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