A3Manga: decrypt page URLs for whole multisrc, update domains (#19160)
A3Manga: decrypt page URLs for whole multisrc
This commit is contained in:
parent
a17eee9d8c
commit
10a45da680
|
@ -1,52 +0,0 @@
|
||||||
package eu.kanade.tachiyomi.extension.vi.ocumeo
|
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.multisrc.a3manga.A3Manga
|
|
||||||
import eu.kanade.tachiyomi.source.model.Page
|
|
||||||
import org.jsoup.Jsoup
|
|
||||||
import org.jsoup.nodes.Document
|
|
||||||
|
|
||||||
class OCuMeo : A3Manga("Ổ Cú Mèo", "https://www.ocumoe.com", "vi") {
|
|
||||||
|
|
||||||
override fun pageListParse(document: Document): List<Page> {
|
|
||||||
val imgListHtml = decodeImgList(document)
|
|
||||||
|
|
||||||
return Jsoup.parseBodyFragment(imgListHtml).select("img").mapIndexed { idx, element ->
|
|
||||||
val encryptedUrl = element.attributes().find { it.key.startsWith("data") }?.value
|
|
||||||
Page(idx, imageUrl = encryptedUrl?.decodeUrl())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun String.decodeUrl(): String {
|
|
||||||
// We expect the URL to start with `https://`, where the last 3 characters are encoded.
|
|
||||||
// The length of the encoded character is not known, but it is the same across all.
|
|
||||||
// Essentially we are looking for the two encoded slashes, which tells us the length.
|
|
||||||
val patternIdx = patternsLengthCheck.indexOfFirst { pattern ->
|
|
||||||
val matchResult = pattern.find(this)
|
|
||||||
val g1 = matchResult?.groupValues?.get(1)
|
|
||||||
val g2 = matchResult?.groupValues?.get(2)
|
|
||||||
g1 == g2 && g1 != null
|
|
||||||
}
|
|
||||||
if (patternIdx == -1) {
|
|
||||||
throw Exception("Failed to decrypt URL")
|
|
||||||
}
|
|
||||||
|
|
||||||
// With a known length we can predict all the encoded characters.
|
|
||||||
// This is a slightly more expensive pattern, hence the separation.
|
|
||||||
val matchResult = patternsSubstitution[patternIdx].find(this)
|
|
||||||
return matchResult?.destructured?.let { (colon, slash, period) ->
|
|
||||||
this
|
|
||||||
.replace(colon, ":")
|
|
||||||
.replace(slash, "/")
|
|
||||||
.replace(period, ".")
|
|
||||||
} ?: throw Exception("Failed to reconstruct URL")
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val patternsLengthCheck: List<Regex> = (20 downTo 1).map { i ->
|
|
||||||
"""^https.{$i}(.{$i})(.{$i})""".toRegex()
|
|
||||||
}
|
|
||||||
private val patternsSubstitution: List<Regex> = (20 downTo 1).map { i ->
|
|
||||||
"""^https(.{$i})(.{$i}).*(.{$i})(?:webp|jpeg|tiff|.{3})$""".toRegex()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -181,8 +181,35 @@ open class A3Manga(
|
||||||
override fun pageListParse(document: Document): List<Page> {
|
override fun pageListParse(document: Document): List<Page> {
|
||||||
val imgListHtml = decodeImgList(document)
|
val imgListHtml = decodeImgList(document)
|
||||||
|
|
||||||
return Jsoup.parseBodyFragment(imgListHtml).select("img").mapIndexed { idx, it ->
|
return Jsoup.parseBodyFragment(imgListHtml).select("img").mapIndexed { idx, element ->
|
||||||
Page(idx, imageUrl = it.attr("abs:src"))
|
val encryptedUrl = element.attributes().find { it.key.startsWith("data") }?.value
|
||||||
|
val effectiveUrl = encryptedUrl?.decodeUrl() ?: element.attr("abs:src")
|
||||||
|
Page(idx, imageUrl = effectiveUrl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun String.decodeUrl(): String? {
|
||||||
|
// We expect the URL to start with `https://`, where the last 3 characters are encoded.
|
||||||
|
// The length of the encoded character is not known, but it is the same across all.
|
||||||
|
// Essentially we are looking for the two encoded slashes, which tells us the length.
|
||||||
|
val patternIdx = patternsLengthCheck.indexOfFirst { pattern ->
|
||||||
|
val matchResult = pattern.find(this)
|
||||||
|
val g1 = matchResult?.groupValues?.get(1)
|
||||||
|
val g2 = matchResult?.groupValues?.get(2)
|
||||||
|
g1 == g2 && g1 != null
|
||||||
|
}
|
||||||
|
if (patternIdx == -1) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
// With a known length we can predict all the encoded characters.
|
||||||
|
// This is a slightly more expensive pattern, hence the separation.
|
||||||
|
val matchResult = patternsSubstitution[patternIdx].find(this)
|
||||||
|
return matchResult?.destructured?.let { (colon, slash, period) ->
|
||||||
|
this
|
||||||
|
.replace(colon, ":")
|
||||||
|
.replace(slash, "/")
|
||||||
|
.replace(period, ".")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -209,5 +236,12 @@ open class A3Manga(
|
||||||
val dateFormat = SimpleDateFormat("dd/MM/yyyy", Locale.US).apply {
|
val dateFormat = SimpleDateFormat("dd/MM/yyyy", Locale.US).apply {
|
||||||
timeZone = TimeZone.getTimeZone("Asia/Ho_Chi_Minh")
|
timeZone = TimeZone.getTimeZone("Asia/Ho_Chi_Minh")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val patternsLengthCheck: List<Regex> = (20 downTo 1).map { i ->
|
||||||
|
"""^https.{$i}(.{$i})(.{$i})""".toRegex()
|
||||||
|
}
|
||||||
|
private val patternsSubstitution: List<Regex> = (20 downTo 1).map { i ->
|
||||||
|
"""^https(.{$i})(.{$i}).*(.{$i})(?:webp|jpeg|tiff|.{3})$""".toRegex()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,11 +9,11 @@ class A3MangaGenerator : ThemeSourceGenerator {
|
||||||
|
|
||||||
override val themeClass = "A3Manga"
|
override val themeClass = "A3Manga"
|
||||||
|
|
||||||
override val baseVersionCode: Int = 1
|
override val baseVersionCode: Int = 2
|
||||||
|
|
||||||
override val sources = listOf(
|
override val sources = listOf(
|
||||||
SingleLang("A3 Manga", "https://www.a3mnga.com", "vi"),
|
SingleLang("A3 Manga", "https://www.a3manga.info", "vi"),
|
||||||
SingleLang("Team Lanh Lung", "https://teamlanhlung.vip", "vi", sourceName = "Team Lạnh Lùng", overrideVersionCode = 1),
|
SingleLang("Team Lanh Lung", "https://teamlanhlung.me", "vi", sourceName = "Team Lạnh Lùng", overrideVersionCode = 1),
|
||||||
SingleLang("Ngon Phong", "https://www.ngonphong.com", "vi", sourceName = "Ngôn Phong", overrideVersionCode = 1),
|
SingleLang("Ngon Phong", "https://www.ngonphong.com", "vi", sourceName = "Ngôn Phong", overrideVersionCode = 1),
|
||||||
SingleLang("O Cu Meo", "https://www.ocumoe.com", "vi", sourceName = "Ổ Cú Mèo", overrideVersionCode = 1),
|
SingleLang("O Cu Meo", "https://www.ocumoe.com", "vi", sourceName = "Ổ Cú Mèo", overrideVersionCode = 1),
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in New Issue