Update 6Manhua parser (#8539)
* Update 6Manhua parser * Code review changes * Code review changes * Code review changes
This commit is contained in:
parent
6433c41cb7
commit
2edb3b6164
@ -1,9 +1,7 @@
|
||||
ext {
|
||||
extName = '6Manhua'
|
||||
extClass = '.SixMH'
|
||||
themePkg = 'mccms'
|
||||
baseUrl = 'https://www.liumanhua.com'
|
||||
overrideVersionCode = 5
|
||||
extVersionCode = 13
|
||||
isNsfw = true
|
||||
}
|
||||
|
||||
|
@ -1,18 +1,24 @@
|
||||
package eu.kanade.tachiyomi.extension.zh.sixmh
|
||||
|
||||
import android.util.Base64
|
||||
import kotlin.experimental.xor
|
||||
import javax.crypto.Cipher
|
||||
import javax.crypto.spec.IvParameterSpec
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
|
||||
private val keys = arrayOf("Ni1iWGQ5aU4=", "Ni1SWHlqcnk=", "Ni1vWXZ3Vnk=", "Ni00Wlk1N1U=", "Ni1tYkpwVTc=", "Ni02TU0yRWk=", "Ni01NFRpUXI=", "Ni1QaDV4eDk=", "Ni1iWWdlUFI=", "Ni1aOUEzYlc=")
|
||||
internal fun decodeData(encodedData: String): String {
|
||||
// Key derived from https://www.liumanhua.com/template/pc/liumanhua/js/index-v2.js line 571
|
||||
// excerpt:
|
||||
// var _0x493e85 = CryptoJS[_0x5b6369(0x15c,'a#uL')]['Utf8'][_0x5b6369(0x147,'H0qZ')](_0x5b6369(0x152,')X69'));
|
||||
// _0x5b6369(0x152,')X69') provides the key
|
||||
val aesKey = "9S8\$vJnU2ANeSRoF"
|
||||
val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
|
||||
val secretKeySpec = SecretKeySpec(aesKey.toByteArray(), "AES")
|
||||
|
||||
internal fun decodeData(encodedData: String, cid: Int): String {
|
||||
val key = Base64.decode(keys[cid % keys.size], Base64.DEFAULT)
|
||||
val keyLength = key.size
|
||||
val decodedData = Base64.decode(encodedData, Base64.DEFAULT)
|
||||
val decryptedData = StringBuilder()
|
||||
for (i in decodedData.indices) {
|
||||
val decryptedCharCode = decodedData[i] xor key[i % keyLength]
|
||||
decryptedData.appendCodePoint(decryptedCharCode.toInt())
|
||||
}
|
||||
return Base64.decode(decryptedData.toString(), Base64.DEFAULT).decodeToString()
|
||||
val decodedBase64 = Base64.decode(encodedData, Base64.DEFAULT)
|
||||
val iv = IvParameterSpec(decodedBase64.sliceArray(0 until 16))
|
||||
val cipherText = decodedBase64.sliceArray(16 until decodedBase64.size)
|
||||
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, iv)
|
||||
val decryptedText = cipher.doFinal(cipherText)
|
||||
|
||||
return decryptedText.toString(Charsets.UTF_8)
|
||||
}
|
||||
|
@ -3,4 +3,6 @@ package eu.kanade.tachiyomi.extension.zh.sixmh
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class Image(val id: String, val url: String)
|
||||
data class Data(
|
||||
val images: List<String>,
|
||||
)
|
@ -0,0 +1,27 @@
|
||||
package eu.kanade.tachiyomi.extension.zh.sixmh
|
||||
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
|
||||
import org.jsoup.nodes.Document
|
||||
import org.jsoup.nodes.Element
|
||||
|
||||
abstract class SimpleParsedHttpSource : ParsedHttpSource() {
|
||||
|
||||
abstract fun simpleMangaSelector(): String
|
||||
abstract fun simpleMangaFromElement(element: Element): SManga
|
||||
abstract fun simpleNextPageSelector(): String?
|
||||
|
||||
override fun popularMangaSelector() = simpleMangaSelector()
|
||||
override fun popularMangaFromElement(element: Element) = simpleMangaFromElement(element)
|
||||
override fun popularMangaNextPageSelector() = simpleNextPageSelector()
|
||||
|
||||
override fun latestUpdatesSelector() = simpleMangaSelector()
|
||||
override fun latestUpdatesFromElement(element: Element) = simpleMangaFromElement(element)
|
||||
override fun latestUpdatesNextPageSelector() = simpleNextPageSelector()
|
||||
|
||||
override fun searchMangaSelector() = simpleMangaSelector()
|
||||
override fun searchMangaFromElement(element: Element) = simpleMangaFromElement(element)
|
||||
override fun searchMangaNextPageSelector() = simpleNextPageSelector()
|
||||
|
||||
override fun imageUrlParse(document: Document): String = throw UnsupportedOperationException()
|
||||
}
|
@ -1,38 +1,80 @@
|
||||
package eu.kanade.tachiyomi.extension.zh.sixmh
|
||||
|
||||
import android.app.Application
|
||||
import android.os.Build
|
||||
import eu.kanade.tachiyomi.multisrc.mccms.MCCMS
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.source.model.FilterList
|
||||
import eu.kanade.tachiyomi.source.model.Page
|
||||
import eu.kanade.tachiyomi.source.model.SChapter
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import kotlinx.serialization.json.Json
|
||||
import keiyoushi.utils.parseAs
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import org.jsoup.nodes.Document
|
||||
import org.jsoup.nodes.Element
|
||||
|
||||
class SixMH : MCCMS("六漫画", "https://www.liumanhua.com") {
|
||||
private val dataRegex = Regex("var DATA = '([A-Za-z0-9+/=]+)'")
|
||||
private val json by injectLazy<Json>()
|
||||
override val versionId get() = 2
|
||||
class SixMH : SimpleParsedHttpSource() {
|
||||
private val paramsRegex = Regex("params = '([A-Za-z0-9+/=]+)'")
|
||||
override val versionId get() = 3
|
||||
override val name: String = "六漫画"
|
||||
override val lang: String = "zh"
|
||||
override val supportsLatest: Boolean = true
|
||||
override val baseUrl: String = "https://www.liumanhua.com"
|
||||
|
||||
init {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
// Delete old preferences for "6漫画/zh/1"
|
||||
Injekt.get<Application>().deleteSharedPreferences("source_7259486566651312186")
|
||||
override fun popularMangaRequest(page: Int) = GET("$baseUrl/category/order/hits/page/$page", headers)
|
||||
override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/category/order/addtime/page/$page", headers)
|
||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
||||
val url = "$baseUrl/index.php/search".toHttpUrl().newBuilder()
|
||||
.addQueryParameter("key", query)
|
||||
.build()
|
||||
return GET(url, headers)
|
||||
}
|
||||
|
||||
override fun simpleNextPageSelector(): String? = null
|
||||
override fun simpleMangaSelector(): String = "div.cy_list_mh ul"
|
||||
override fun simpleMangaFromElement(element: Element): SManga = SManga.create().apply {
|
||||
title = element.selectFirst("li.title > a")!!.text()
|
||||
setUrlWithoutDomain(element.selectFirst("li.title > a")!!.absUrl("href"))
|
||||
thumbnail_url = element.selectFirst("img")?.absUrl("src")
|
||||
}
|
||||
|
||||
// Details
|
||||
override fun mangaDetailsParse(document: Document): SManga = SManga.create().apply {
|
||||
val element = document.selectFirst("div.cy_info")!!
|
||||
title = element.selectFirst("div.cy_title")!!.text()
|
||||
thumbnail_url = element.selectFirst("div.cy_info_cover > a > img.pic")?.absUrl("src")
|
||||
description = element.selectFirst("div.cy_desc #comic-description")?.text()
|
||||
|
||||
val infoElements = element.select("div.cy_xinxi")
|
||||
author = infoElements[0].selectFirst("span:first-child > a")?.text()
|
||||
status = parseStatus(infoElements[0].selectFirst("span:nth-child(2)")?.text())
|
||||
genre = infoElements[1].selectFirst("span:first-child > a")?.text()
|
||||
}
|
||||
|
||||
// Chapters
|
||||
override fun chapterListSelector(): String = "ul#mh-chapter-list-ol-0 li.chapter__item"
|
||||
override fun chapterFromElement(element: Element): SChapter = SChapter.create().apply {
|
||||
setUrlWithoutDomain(element.selectFirst("a")!!.absUrl("href"))
|
||||
name = element.selectFirst("a > p")!!.text()
|
||||
}
|
||||
|
||||
// Pages
|
||||
override fun pageListParse(response: Response): List<Page> {
|
||||
val body = response.body.string()
|
||||
val encodedData = paramsRegex.find(body)?.groupValues?.get(1) ?: ""
|
||||
val decodedData = decodeData(encodedData)
|
||||
|
||||
val images = decodedData.parseAs<Data>().images
|
||||
return images.mapIndexed { index, url -> Page(index, imageUrl = url) }
|
||||
}
|
||||
|
||||
override fun pageListParse(document: Document): List<Page> = throw UnsupportedOperationException()
|
||||
|
||||
private fun parseStatus(status: String?): Int {
|
||||
return when {
|
||||
status == null -> SManga.UNKNOWN
|
||||
status.contains("连载") -> SManga.ONGOING
|
||||
status.contains("完结") -> SManga.COMPLETED
|
||||
else -> SManga.UNKNOWN
|
||||
}
|
||||
}
|
||||
|
||||
override fun getMangaUrl(manga: SManga) = "https://m.liumanhua.com" + manga.url
|
||||
override fun getChapterUrl(chapter: SChapter) = "https://m.liumanhua.com" + chapter.url
|
||||
|
||||
override fun pageListParse(response: Response): List<Page> {
|
||||
val encodedData = dataRegex.find(response.body.string())?.groupValues?.get(1) ?: ""
|
||||
val cid = response.request.url.pathSegments.last().removeSuffix(".html").toIntOrNull() ?: 0
|
||||
val decodedData = decodeData(encodedData, cid)
|
||||
val images = json.decodeFromString<List<Image>>(decodedData)
|
||||
return images.mapIndexed { index, image -> Page(index, imageUrl = image.url) }
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user