From a6633e2545b9fc7080e9524db0f1486d49061f2c Mon Sep 17 00:00:00 2001 From: Mike <51273546+SnakeDoc83@users.noreply.github.com> Date: Fri, 3 Apr 2020 20:47:50 -0400 Subject: [PATCH] Mangahere - revert & tweak pages (#2582) Mangahere - revert & tweak pages --- src/en/mangahere/build.gradle | 2 +- .../extension/en/mangahere/Mangahere.kt | 121 ++++++++++++++++-- 2 files changed, 111 insertions(+), 12 deletions(-) diff --git a/src/en/mangahere/build.gradle b/src/en/mangahere/build.gradle index 9446c6dce..b5be67039 100644 --- a/src/en/mangahere/build.gradle +++ b/src/en/mangahere/build.gradle @@ -5,7 +5,7 @@ ext { appName = 'Tachiyomi: Mangahere' pkgNameSuffix = 'en.mangahere' extClass = '.Mangahere' - extVersionCode = 12 + extVersionCode = 13 libVersion = '1.2' } diff --git a/src/en/mangahere/src/eu/kanade/tachiyomi/extension/en/mangahere/Mangahere.kt b/src/en/mangahere/src/eu/kanade/tachiyomi/extension/en/mangahere/Mangahere.kt index 41a833779..6e9844976 100644 --- a/src/en/mangahere/src/eu/kanade/tachiyomi/extension/en/mangahere/Mangahere.kt +++ b/src/en/mangahere/src/eu/kanade/tachiyomi/extension/en/mangahere/Mangahere.kt @@ -8,6 +8,8 @@ import eu.kanade.tachiyomi.util.asJsoup import okhttp3.* import org.jsoup.nodes.Document import org.jsoup.nodes.Element +import java.lang.NumberFormatException +import java.lang.UnsupportedOperationException import java.text.ParseException import java.text.SimpleDateFormat import java.util.* @@ -25,7 +27,7 @@ class Mangahere : ParsedHttpSource() { override val supportsLatest = true - override val client = super.client.newBuilder() + override val client: OkHttpClient = super.client.newBuilder() .cookieJar(object : CookieJar{ override fun saveFromResponse(url: HttpUrl, cookies: MutableList) {} override fun loadForRequest(url: HttpUrl): MutableList { @@ -96,7 +98,7 @@ class Mangahere : ParsedHttpSource() { } url.addEncodedQueryParameter("genres", includeGenres.joinToString(",")) - .addEncodedQueryParameter("nogenres", excludeGenres.joinToString(",")) + .addEncodedQueryParameter("nogenres", excludeGenres.joinToString(",")) } } @@ -191,19 +193,116 @@ class Mangahere : ParsedHttpSource() { } } - override fun pageListRequest(chapter: SChapter): Request { - return GET("$baseUrl/${chapter.url}".replace("www","m"), headers) + override fun pageListParse(document: Document): List { + val bar = document.select("script[src*=chapter_bar]") + val duktape = Duktape.create() + + // if-branch is for webtoon reader, else is for page-by-page + return if (bar.isNotEmpty()) { + val script = document.select("script:containsData(function(p,a,c,k,e,d))").html().removePrefix("eval") + val deobfuscatedScript = duktape.evaluate(script).toString() + val urls = deobfuscatedScript.substringAfter("newImgs=['").substringBefore("'];").split("','") + duktape.close() + + /* + last webtoon imageUrl is usually broken, working imageUrls are incremental (e.g. t001, t002, etc); if the difference between + the last two isn't 1 or doesn't have an Int at the end of the last imageUrl's filename, drop last Page + */ + urls.mapIndexed { index, s -> Page(index, "", "https:$s") }.let { pages -> + val list = pages.takeLast(2).map { page -> + try { + page.imageUrl!!.substringBeforeLast(".").substringAfterLast("/").takeLast(2).toInt() + } catch (_: NumberFormatException) { + return pages.dropLast(1) + } + } + when { + list[0] == 0 && 100 - list[1] == 1 -> pages + list[1] - list[0] == 1 -> pages + else -> pages.dropLast(1) + } + } + } else { + val html = document.html() + val link = document.location() + + var secretKey = extractSecretKey(html, duktape) + + val chapterIdStartLoc = html.indexOf("chapterid") + val chapterId = html.substring( + chapterIdStartLoc + 11, + html.indexOf(";", chapterIdStartLoc)).trim() + + val chapterPagesElement = document.select(".pager-list-left > span").first() + val pagesLinksElements = chapterPagesElement.select("a") + val pagesNumber = pagesLinksElements[pagesLinksElements.size - 2].attr("data-page").toInt() + + val pageBase = link.substring(0, link.lastIndexOf("/")) + + IntRange(1, pagesNumber).map { i -> + + val pageLink = "${pageBase}/chapterfun.ashx?cid=$chapterId&page=$i&key=$secretKey" + + var responseText = "" + + for (tr in 1..3){ + + val request = Request.Builder() + .url(pageLink) + .addHeader("Referer",link) + .addHeader("Accept","*/*") + .addHeader("Accept-Language","en-US,en;q=0.9") + .addHeader("Connection","keep-alive") + .addHeader("Host","www.mangahere.cc") + .addHeader("User-Agent", System.getProperty("http.agent") ?: "") + .addHeader("X-Requested-With","XMLHttpRequest") + .build() + + val response = client.newCall(request).execute() + responseText = response.body()!!.string() + + if (responseText.isNotEmpty()) + break + else + secretKey = "" + + } + + val deobfuscatedScript = duktape.evaluate(responseText.removePrefix("eval")).toString() + + val baseLinkStartPos = deobfuscatedScript.indexOf("pix=") + 5 + val baseLinkEndPos = deobfuscatedScript.indexOf(";", baseLinkStartPos) - 1 + val baseLink = deobfuscatedScript.substring(baseLinkStartPos, baseLinkEndPos) + + val imageLinkStartPos = deobfuscatedScript.indexOf("pvalue=") + 9 + val imageLinkEndPos = deobfuscatedScript.indexOf("\"", imageLinkStartPos) + val imageLink = deobfuscatedScript.substring(imageLinkStartPos, imageLinkEndPos) + + Page(i - 1, "", "https:$baseLink$imageLink") + + } + }.also { duktape.close() } } - override fun pageListParse(document: Document): List = mutableListOf().apply { - document.select("select option").forEach { - add(Page(size,"https:${it.attr("value")}")) - } + private fun extractSecretKey(html: String, duktape: Duktape): String { + + val secretKeyScriptLocation = html.indexOf("eval(function(p,a,c,k,e,d)") + val secretKeyScriptEndLocation = html.indexOf("", secretKeyScriptLocation) + val secretKeyScript = html.substring(secretKeyScriptLocation, secretKeyScriptEndLocation).removePrefix("eval") + + val secretKeyDeobfuscatedScript = duktape.evaluate(secretKeyScript).toString() + + val secretKeyStartLoc = secretKeyDeobfuscatedScript.indexOf("'") + val secretKeyEndLoc = secretKeyDeobfuscatedScript.indexOf(";") + + val secretKeyResultScript = secretKeyDeobfuscatedScript.substring( + secretKeyStartLoc, secretKeyEndLoc) + + return duktape.evaluate(secretKeyResultScript).toString() + } - override fun imageUrlParse(document: Document): String { - return document.select("img#image").attr("src") - } + override fun imageUrlParse(document: Document): String = throw UnsupportedOperationException("Not used") private class Genre(title: String, val id: Int) : Filter.TriState(title)