FMReader Fixes Pt.2: The Encryption Strikes Back (#7067)

* FMReader: fix fetchPageListEncrypted

* Add HARSH rate limiting to ManhwaHot, ManhuaScan, HeroScan

* Don't specify period=

* Bump versions
This commit is contained in:
h-hyuuga 2021-05-17 15:50:05 -04:00 committed by GitHub
parent 270745cc5b
commit 89e9bd43a9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 37 additions and 20 deletions

View File

@ -0,0 +1,4 @@
dependencies {
implementation project(':lib-ratelimit')
}

View File

@ -1,11 +1,13 @@
package eu.kanade.tachiyomi.extension.en.heroscan
import eu.kanade.tachiyomi.lib.ratelimit.RateLimitInterceptor
import eu.kanade.tachiyomi.multisrc.fmreader.FMReader
import eu.kanade.tachiyomi.source.model.SChapter
import okhttp3.OkHttpClient
class HeroScan : FMReader("HeroScan", "https://heroscan.com", "en") {
override val client: OkHttpClient = super.client.newBuilder()
.addInterceptor(RateLimitInterceptor(1))
.addInterceptor { chain ->
val originalRequest = chain.request()
chain.proceed(originalRequest).let { response ->

View File

@ -0,0 +1,4 @@
dependencies {
implementation project(':lib-ratelimit')
}

View File

@ -1,12 +1,15 @@
package eu.kanade.tachiyomi.extension.en.manhuascan
import eu.kanade.tachiyomi.lib.ratelimit.RateLimitInterceptor
import eu.kanade.tachiyomi.multisrc.fmreader.FMReader
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.annotations.Nsfw
import okhttp3.OkHttpClient
@Nsfw
class ManhuaScan : FMReader("ManhuaScan", "https://manhuascan.com", "en") {
override val client: OkHttpClient = super.client.newBuilder()
.addInterceptor(RateLimitInterceptor(1))
.build()
override fun fetchPageList(chapter: SChapter) = fetchPageListEncrypted(chapter)
}

View File

@ -0,0 +1,4 @@
dependencies {
implementation project(':lib-ratelimit')
}

View File

@ -1,13 +1,15 @@
package eu.kanade.tachiyomi.extension.en.manhwahot
import eu.kanade.tachiyomi.lib.ratelimit.RateLimitInterceptor
import eu.kanade.tachiyomi.annotations.Nsfw
import eu.kanade.tachiyomi.multisrc.fmreader.FMReader
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.source.model.Page
import okhttp3.Request
import eu.kanade.tachiyomi.source.model.SChapter
import okhttp3.OkHttpClient
@Nsfw
class ManhwaHot : FMReader("ManhwaHot", "https://manhwahot.com", "en") {
override val client: OkHttpClient = super.client.newBuilder()
.addInterceptor(RateLimitInterceptor(1))
.build()
override fun fetchPageList(chapter: SChapter) = fetchPageListEncrypted(chapter)
}

View File

@ -30,6 +30,7 @@ import rx.Observable
import java.nio.charset.Charset
import java.security.MessageDigest
import java.util.Calendar
import javax.crypto.BadPaddingException
import javax.crypto.Cipher
import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.SecretKeySpec
@ -333,26 +334,23 @@ abstract class FMReader(
* e.g ManhuaScan, HeroScan
*/
protected fun fetchPageListEncrypted(chapter: SChapter): Observable<List<Page>> {
fun stringAssignment(varname: String, script: String) = Regex("""var\s+$varname\s*=\s*"([^"]*)"""").find(script)?.groups?.get(1)?.value
fun stringAssignment(varname: String, script: String) = Regex("""(?:let|var)\s+$varname\s*=\s*"([^"]*)"""").find(script)?.groups?.get(1)?.value
fun pageList(s: String) = Regex("https.+?(?=https|\"$)").findAll(s).map { it.groups[0]!!.value.replace("\\/", "/") }
fun pageListRequest(id: String, server: Int = 1) = POST("$baseUrl/app/manga/controllers/cont.chapterServer$server.php", headers, "id=$id".toRequestBody("application/x-www-form-urlencoded; charset=UTF-8".toMediaTypeOrNull()))
return client.newCall(GET("$baseUrl${chapter.url}", headers)).asObservableSuccess().concatMap { htmlResponse ->
val soup = htmlResponse.asJsoup()
soup.selectFirst("head > script[type='text/javascript']")?.data()?.let { params ->
val chapterId = stringAssignment("chapter_id", params)
val csrfToken = stringAssignment("csrf_token", params)
if (chapterId == null || csrfToken == null)
null
else
stringAssignment("chapter_id", params)?.let { chapterId ->
client.newCall(pageListRequest(chapterId)).asObservableSuccess()
.map { jsonResponse ->
pageList(
crypto.aes_decrypt(
jsonResponse.body!!.string(),
crypto.md5("$csrfToken$csrfToken").toByteArray()
)
).mapIndexed { i, imgUrl -> Page(i, "", imgUrl) }.toList()
try {
pageList(crypto.aes_decrypt(jsonResponse.body!!.string(), "4xje8fvkub2d3mb5cy9rv661zyjakbcn".toByteArray()))
.mapIndexed { i, imgUrl -> Page(i, "", imgUrl) }.toList()
} catch (_: BadPaddingException) {
throw RuntimeException("Decryption Failed")
}
}
}
} ?: Observable.just(emptyList())
}
}

View File

@ -17,17 +17,17 @@ class FMReaderGenerator : ThemeSourceGenerator {
override val sources = listOf(
SingleLang("Epik Manga", "https://www.epikmanga.com", "tr"),
SingleLang("HeroScan", "https://heroscan.com", "en", overrideVersionCode = 1),
SingleLang("HeroScan", "https://heroscan.com", "en", overrideVersionCode = 2),
SingleLang("KissLove", "https://kissaway.net", "ja"),
SingleLang("LHTranslation", "https://lhtranslation.net", "en", overrideVersionCode = 1),
SingleLang("Manga-TR", "https://manga-tr.com", "tr", className = "MangaTR"),
SingleLang("ManhuaScan", "https://manhuascan.com", "en", isNsfw = true, overrideVersionCode = 2),
SingleLang("ManhuaScan", "https://manhuascan.com", "en", isNsfw = true, overrideVersionCode = 3),
SingleLang("Manhwa18", "https://manhwa18.com", "en", isNsfw = true),
MultiLang("Manhwa18.net", "https://manhwa18.net", listOf("en", "ko"), className = "Manhwa18NetFactory", isNsfw = true),
SingleLang("RawLH", "https://lovehug.net", "ja"),
SingleLang("Say Truyen", "https://saytruyen.com", "vi"),
SingleLang("KSGroupScans", "https://ksgroupscans.com", "en"),
SingleLang("ManhwaHot", "https://manhwahot.com", "en", isNsfw = true),
SingleLang("ManhwaHot", "https://manhwahot.com", "en", isNsfw = true, overrideVersionCode=1),
// Sites that went down
//SingleLang("18LHPlus", "https://18lhplus.com", "en", className = "EighteenLHPlus"),
//SingleLang("HanaScan (RawQQ)", "https://hanascan.com", "ja", className = "HanaScanRawQQ"),