Remove dead sources (#8875)
* Remove Mangaku * Remove MangaRuhu * Remove PussyToons
1
src/id/mangaku/assets/crypto-js.min.js
vendored
2
src/id/mangaku/assets/zepto.min.js
vendored
@ -1,7 +0,0 @@
|
||||
ext {
|
||||
extName = 'Mangaku'
|
||||
extClass = '.Mangaku'
|
||||
extVersionCode = 10
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
Before Width: | Height: | Size: 6.5 KiB |
Before Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 37 KiB |
@ -1,255 +0,0 @@
|
||||
package eu.kanade.tachiyomi.extension.id.mangaku
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Application
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.view.View
|
||||
import android.webkit.JavascriptInterface
|
||||
import android.webkit.WebView
|
||||
import android.webkit.WebViewClient
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.network.POST
|
||||
import eu.kanade.tachiyomi.network.asObservableSuccess
|
||||
import eu.kanade.tachiyomi.source.model.FilterList
|
||||
import eu.kanade.tachiyomi.source.model.MangasPage
|
||||
import eu.kanade.tachiyomi.source.model.Page
|
||||
import eu.kanade.tachiyomi.source.model.SChapter
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
|
||||
import eu.kanade.tachiyomi.util.asJsoup
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.jsonArray
|
||||
import kotlinx.serialization.json.jsonPrimitive
|
||||
import okhttp3.FormBody
|
||||
import okhttp3.Headers
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import org.jsoup.nodes.Document
|
||||
import org.jsoup.nodes.Element
|
||||
import org.jsoup.select.Elements
|
||||
import rx.Observable
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class Mangaku : ParsedHttpSource() {
|
||||
|
||||
override val name = "Mangaku"
|
||||
|
||||
override val baseUrl = "https://mangaku.lat"
|
||||
|
||||
override val lang = "id"
|
||||
|
||||
override val supportsLatest = true
|
||||
|
||||
override val client = network.cloudflareClient
|
||||
|
||||
private lateinit var directory: Elements
|
||||
|
||||
override fun headersBuilder(): Headers.Builder =
|
||||
super.headersBuilder().add("Referer", "$baseUrl/")
|
||||
|
||||
override fun fetchPopularManga(page: Int): Observable<MangasPage> {
|
||||
return if (page == 1) {
|
||||
client.newCall(popularMangaRequest(page))
|
||||
.asObservableSuccess()
|
||||
.map { popularMangaParse(it) }
|
||||
} else {
|
||||
Observable.just(parseDirectory(page))
|
||||
}
|
||||
}
|
||||
|
||||
override fun popularMangaRequest(page: Int): Request =
|
||||
POST(
|
||||
"$baseUrl/daftar-komik-bahasa-indonesia/",
|
||||
headers,
|
||||
FormBody.Builder().add("ritem", "hot").build(),
|
||||
)
|
||||
|
||||
override fun popularMangaParse(response: Response): MangasPage {
|
||||
val document = response.asJsoup()
|
||||
directory = document.select(popularMangaSelector())
|
||||
return parseDirectory(1)
|
||||
}
|
||||
|
||||
private fun parseDirectory(page: Int): MangasPage {
|
||||
val manga = mutableListOf<SManga>()
|
||||
val end = ((page * 24) - 1).let { if (it <= directory.lastIndex) it else directory.lastIndex }
|
||||
|
||||
for (i in (((page - 1) * 24)..end)) {
|
||||
manga.add(popularMangaFromElement(directory[i]))
|
||||
}
|
||||
return MangasPage(manga, end < directory.lastIndex)
|
||||
}
|
||||
|
||||
override fun popularMangaSelector() = "#data .npx .an a"
|
||||
|
||||
override fun popularMangaFromElement(element: Element): SManga = SManga.create().apply {
|
||||
setUrlWithoutDomain(element.attr("href"))
|
||||
title = element.ownText()
|
||||
thumbnail_url = element.selectFirst("img")?.attr("abs:data-src")
|
||||
}
|
||||
|
||||
override fun popularMangaNextPageSelector(): String? = null
|
||||
|
||||
override fun latestUpdatesRequest(page: Int): Request = GET(baseUrl, headers)
|
||||
|
||||
override fun latestUpdatesSelector() = "div.kiri_anime div.utao"
|
||||
|
||||
override fun latestUpdatesFromElement(element: Element): SManga = SManga.create().apply {
|
||||
setUrlWithoutDomain(element.select("div.uta div.luf a.series").attr("href"))
|
||||
title = element.select("div.uta div.luf a.series").text()
|
||||
thumbnail_url = element.select("div.uta div.imgu img").attr("abs:data-src")
|
||||
}
|
||||
|
||||
override fun latestUpdatesNextPageSelector(): String? = null
|
||||
|
||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request =
|
||||
GET("$baseUrl/search/$query/", headers)
|
||||
|
||||
override fun searchMangaSelector() = ".listupd .bs"
|
||||
|
||||
override fun searchMangaFromElement(element: Element) = SManga.create().apply {
|
||||
setUrlWithoutDomain(element.select(".bsx a").attr("href"))
|
||||
title = element.select(".bigor .tt a").text()
|
||||
thumbnail_url = element.select(".bsx img").attr("abs:data-src")
|
||||
}
|
||||
|
||||
override fun searchMangaNextPageSelector(): String? = null
|
||||
|
||||
override fun mangaDetailsParse(document: Document): SManga = SManga.create().apply {
|
||||
title = document
|
||||
.select("h1.titles a, h1.title").text()
|
||||
.replace("Bahasa Indonesia", "").trim()
|
||||
|
||||
thumbnail_url = document
|
||||
.select("#sidebar-a a[imageanchor] > img, #abc a[imageanchor] > img")
|
||||
.attr("abs:src")
|
||||
|
||||
genre = document.select(".inf:contains(Genre) p a, .inf:contains(Type) p").joinToString { it.text() }
|
||||
document.select("#wrapper-a #content-a .inf, #abc .inf").forEach { row ->
|
||||
when (row.select(".infx").text()) {
|
||||
"Author" -> author = row.select("p").text()
|
||||
"Sinopsis" -> description = row.select("p").text()
|
||||
}
|
||||
}
|
||||
val altName = document.selectFirst(".inf:contains(Alternative) p")?.ownText().takeIf { it.isNullOrBlank().not() }
|
||||
altName?.let {
|
||||
description = "$description\n\nAlternative Name: $altName".trim()
|
||||
}
|
||||
}
|
||||
|
||||
override fun chapterListSelector() = "#content-b > div > a, .fndsosmed-social + div > a"
|
||||
|
||||
override fun chapterFromElement(element: Element): SChapter = SChapter.create().apply {
|
||||
setUrlWithoutDomain(element.attr("href"))
|
||||
name = element.text().let {
|
||||
if (it.contains("–")) {
|
||||
it.split("–")[1].trim()
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("SetJavaScriptEnabled")
|
||||
override fun pageListParse(document: Document): List<Page> {
|
||||
val interfaceName = randomString()
|
||||
|
||||
val decodeScriptOriginal = document
|
||||
.select("script:containsData(dtx = )")
|
||||
.joinToString("\n") { it.data() }
|
||||
val decodeScript = decodeScriptOriginal.replace(urlsnxRe) {
|
||||
it.value + "window.$interfaceName.passPayload(JSON.stringify(urlsnx));"
|
||||
}
|
||||
|
||||
val wpRoutineUrl = document
|
||||
.selectFirst("script[src*=wp-routine]")!!
|
||||
.attr("abs:src")
|
||||
val wpRoutineScript = client
|
||||
.newCall(GET(wpRoutineUrl, headers))
|
||||
.execute().use { it.body.string() }
|
||||
|
||||
val handler = Handler(Looper.getMainLooper())
|
||||
val latch = CountDownLatch(1)
|
||||
val jsInterface = JsInterface(latch)
|
||||
var webView: WebView? = null
|
||||
|
||||
handler.post {
|
||||
val webview = WebView(Injekt.get<Application>())
|
||||
webView = webview
|
||||
webview.settings.javaScriptEnabled = true
|
||||
webview.settings.blockNetworkLoads = true
|
||||
webview.settings.blockNetworkImage = true
|
||||
webview.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
|
||||
webview.addJavascriptInterface(jsInterface, interfaceName)
|
||||
|
||||
webview.webViewClient = object : WebViewClient() {
|
||||
override fun onPageFinished(view: WebView, url: String) {
|
||||
view.evaluateJavascript(jQueryScript) {}
|
||||
view.evaluateJavascript(cryptoJSScript) {}
|
||||
view.evaluateJavascript(wpRoutineScript) {}
|
||||
view.evaluateJavascript(decodeScript) {}
|
||||
}
|
||||
}
|
||||
webview.loadDataWithBaseURL(
|
||||
document.location(),
|
||||
"",
|
||||
"text/html",
|
||||
"UTF-8",
|
||||
null,
|
||||
)
|
||||
}
|
||||
|
||||
// 5s is ten times over the execution time on a crappy emulator
|
||||
latch.await(5, TimeUnit.SECONDS)
|
||||
handler.post { webView?.destroy() }
|
||||
|
||||
if (latch.count == 1L) {
|
||||
throw Exception("Kehabisan waktu saat men-decrypt tautan gambar") // Timeout while decrypting image links
|
||||
}
|
||||
|
||||
return jsInterface.images.mapIndexed { i, url ->
|
||||
Page(i, imageUrl = url)
|
||||
}
|
||||
}
|
||||
|
||||
override fun imageUrlParse(document: Document) = throw UnsupportedOperationException()
|
||||
|
||||
private val urlsnxRe = Regex("""urlsnx=(?!\[];)[^;]+;""")
|
||||
|
||||
private fun randomString(length: Int = 10): String {
|
||||
val charPool = ('a'..'z') + ('A'..'Z')
|
||||
return List(length) { charPool.random() }.joinToString("")
|
||||
}
|
||||
|
||||
internal class JsInterface(private val latch: CountDownLatch) {
|
||||
|
||||
private val json: Json by injectLazy()
|
||||
|
||||
var images: List<String> = listOf()
|
||||
private set
|
||||
|
||||
@JavascriptInterface
|
||||
fun passPayload(rawData: String) {
|
||||
val data = json.parseToJsonElement(rawData).jsonArray
|
||||
images = data.map { it.jsonPrimitive.content }
|
||||
latch.countDown()
|
||||
}
|
||||
}
|
||||
|
||||
private val jQueryScript by lazy {
|
||||
javaClass
|
||||
.getResource("/assets/zepto.min.js")!!
|
||||
.readText() // Zepto v1.2.0 (jQuery compatible)
|
||||
}
|
||||
|
||||
private val cryptoJSScript by lazy {
|
||||
javaClass
|
||||
.getResource("/assets/crypto-js.min.js")!!
|
||||
.readText() // CryptoJS v4.0.0 (on site: cpr2.js)
|
||||
}
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
ext {
|
||||
extName = 'PussyToons'
|
||||
extClass = '.PussyToons'
|
||||
themePkg = 'madara'
|
||||
baseUrl = 'https://pussy.sussytoons.com'
|
||||
overrideVersionCode = 1
|
||||
isNsfw = true
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
Before Width: | Height: | Size: 7.1 KiB |
Before Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 42 KiB |
@ -1,17 +0,0 @@
|
||||
package eu.kanade.tachiyomi.extension.pt.pussytoons
|
||||
|
||||
import eu.kanade.tachiyomi.multisrc.madara.Madara
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
|
||||
class PussyToons : Madara(
|
||||
"PussyToons",
|
||||
"https://pussy.sussytoons.com",
|
||||
"pt-BR",
|
||||
dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale("pt", "BR")),
|
||||
) {
|
||||
override val useLoadMoreRequest = LoadMoreStrategy.Never
|
||||
override val useNewChapterEndpoint = true
|
||||
|
||||
override fun popularMangaSelector() = ".main-col-inner div.page-item-detail"
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
ext {
|
||||
extName = 'Manga Ruhu'
|
||||
extClass = '.MangaRuhu'
|
||||
themePkg = 'madara'
|
||||
baseUrl = 'https://mangaruhu.com'
|
||||
overrideVersionCode = 0
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
Before Width: | Height: | Size: 8.4 KiB |
Before Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 40 KiB |
@ -1,14 +0,0 @@
|
||||
package eu.kanade.tachiyomi.extension.tr.mangaruhu
|
||||
|
||||
import eu.kanade.tachiyomi.multisrc.madara.Madara
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
|
||||
class MangaRuhu : Madara(
|
||||
"Manga Ruhu",
|
||||
"https://mangaruhu.com",
|
||||
"tr",
|
||||
SimpleDateFormat("d MMMM yyyy", Locale("tr")),
|
||||
) {
|
||||
override val filterNonMangaItems = false
|
||||
}
|