Remove Kissmanga (#2911)
This commit is contained in:
parent
31c0888972
commit
b54899b2aa
|
@ -1,17 +0,0 @@
|
||||||
apply plugin: 'com.android.application'
|
|
||||||
apply plugin: 'kotlin-android'
|
|
||||||
|
|
||||||
ext {
|
|
||||||
appName = 'Tachiyomi: Kissmanga'
|
|
||||||
pkgNameSuffix = 'en.kissmanga'
|
|
||||||
extClass = '.Kissmanga'
|
|
||||||
extVersionCode = 17
|
|
||||||
libVersion = '1.2'
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
implementation project(':lib-ratelimit')
|
|
||||||
compileOnly project(':duktape-stub')
|
|
||||||
}
|
|
||||||
|
|
||||||
apply from: "$rootDir/common.gradle"
|
|
Binary file not shown.
Before Width: | Height: | Size: 4.1 KiB |
Binary file not shown.
Before Width: | Height: | Size: 2.2 KiB |
Binary file not shown.
Before Width: | Height: | Size: 5.5 KiB |
Binary file not shown.
Before Width: | Height: | Size: 9.9 KiB |
Binary file not shown.
Before Width: | Height: | Size: 16 KiB |
Binary file not shown.
Before Width: | Height: | Size: 62 KiB |
|
@ -1,273 +0,0 @@
|
||||||
package eu.kanade.tachiyomi.extension.en.kissmanga
|
|
||||||
|
|
||||||
import com.squareup.duktape.Duktape
|
|
||||||
import eu.kanade.tachiyomi.lib.ratelimit.RateLimitInterceptor
|
|
||||||
import eu.kanade.tachiyomi.network.GET
|
|
||||||
import eu.kanade.tachiyomi.network.POST
|
|
||||||
import eu.kanade.tachiyomi.source.model.Filter
|
|
||||||
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 eu.kanade.tachiyomi.source.online.ParsedHttpSource
|
|
||||||
import java.text.SimpleDateFormat
|
|
||||||
import java.util.regex.Pattern
|
|
||||||
import okhttp3.FormBody
|
|
||||||
import okhttp3.Headers
|
|
||||||
import okhttp3.OkHttpClient
|
|
||||||
import okhttp3.Request
|
|
||||||
import okhttp3.Response
|
|
||||||
import org.jsoup.Jsoup
|
|
||||||
import org.jsoup.nodes.Document
|
|
||||||
import org.jsoup.nodes.Element
|
|
||||||
|
|
||||||
class Kissmanga : ParsedHttpSource() {
|
|
||||||
|
|
||||||
override val id: Long = 4
|
|
||||||
|
|
||||||
override val name = "Kissmanga"
|
|
||||||
|
|
||||||
override val baseUrl = "https://kissmanga.com"
|
|
||||||
|
|
||||||
override val lang = "en"
|
|
||||||
|
|
||||||
override val supportsLatest = true
|
|
||||||
|
|
||||||
override val client: OkHttpClient = network.cloudflareClient.newBuilder()
|
|
||||||
.addNetworkInterceptor(RateLimitInterceptor(5))
|
|
||||||
.build()
|
|
||||||
|
|
||||||
override fun headersBuilder(): Headers.Builder {
|
|
||||||
return Headers.Builder()
|
|
||||||
.add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.113 Safari/537.36")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun popularMangaSelector() = "table.listing tr:gt(1)"
|
|
||||||
|
|
||||||
override fun latestUpdatesSelector() = "table.listing tr:gt(1)"
|
|
||||||
|
|
||||||
override fun popularMangaRequest(page: Int): Request {
|
|
||||||
return GET("$baseUrl/MangaList/MostPopular?page=$page", headers)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun latestUpdatesRequest(page: Int): Request {
|
|
||||||
return GET("$baseUrl/MangaList/LatestUpdate?page=$page", headers)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun popularMangaFromElement(element: Element): SManga {
|
|
||||||
val manga = SManga.create()
|
|
||||||
manga.thumbnail_url = Jsoup.parseBodyFragment(element.select("td").attr("title")).select("img").attr("abs:src")
|
|
||||||
element.select("td a:eq(0)").first().let {
|
|
||||||
manga.setUrlWithoutDomain(it.attr("href"))
|
|
||||||
val title = it.text()
|
|
||||||
// check if cloudfire email obfuscation is affecting title name
|
|
||||||
if (title.contains("[email protected]", true)) {
|
|
||||||
try {
|
|
||||||
var str: String = it.html()
|
|
||||||
// get the number
|
|
||||||
str = str.substringAfter("data-cfemail=\"")
|
|
||||||
str = str.substringBefore("\">[email")
|
|
||||||
val sb = StringBuilder()
|
|
||||||
// convert number to char
|
|
||||||
val r = Integer.valueOf(str.substring(0, 2), 16)!!
|
|
||||||
var i = 2
|
|
||||||
while (i < str.length) {
|
|
||||||
val c = (Integer.valueOf(str.substring(i, i + 2), 16) xor r).toChar()
|
|
||||||
sb.append(c)
|
|
||||||
i += 2
|
|
||||||
}
|
|
||||||
// replace the new word into the title
|
|
||||||
manga.title = title.replace("[email protected]", sb.toString(), true)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
// on error just default to obfuscated title
|
|
||||||
manga.title = title
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
manga.title = title
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return manga
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun latestUpdatesFromElement(element: Element): SManga {
|
|
||||||
return popularMangaFromElement(element)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun popularMangaNextPageSelector() = "li > a:contains(› Next)"
|
|
||||||
|
|
||||||
override fun latestUpdatesNextPageSelector(): String = "ul.pager > li > a:contains(Next)"
|
|
||||||
|
|
||||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
|
||||||
for (filter in filters) {
|
|
||||||
when (filter) {
|
|
||||||
is SortTrending -> if (filter.state) return GET("$baseUrl/MangaList/Trending?page=$page", headers)
|
|
||||||
is NewManga -> if (filter.state) return GET("$baseUrl/MangaList/Newest?page=$page", headers)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val form = FormBody.Builder().apply {
|
|
||||||
add("mangaName", query)
|
|
||||||
|
|
||||||
for (filter in if (filters.isEmpty()) getFilterList() else filters) {
|
|
||||||
when (filter) {
|
|
||||||
is Author -> add("authorArtist", filter.state)
|
|
||||||
is Status -> add("status", arrayOf("", "Completed", "Ongoing")[filter.state])
|
|
||||||
is GenreList -> filter.state.forEach { genre -> add("genres", genre.state.toString()) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return POST("$baseUrl/AdvanceSearch", headers, form.build())
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun searchMangaSelector() = popularMangaSelector()
|
|
||||||
|
|
||||||
override fun searchMangaFromElement(element: Element): SManga {
|
|
||||||
return popularMangaFromElement(element)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun searchMangaNextPageSelector() = null
|
|
||||||
|
|
||||||
override fun mangaDetailsParse(document: Document): SManga {
|
|
||||||
val infoElement = document.select("div.barContent").first()
|
|
||||||
|
|
||||||
val manga = SManga.create()
|
|
||||||
manga.author = infoElement.select("p:has(span:contains(Author:)) > a").first()?.text()
|
|
||||||
manga.genre = infoElement.select("p:has(span:contains(Genres:)) > *:gt(0)").text()
|
|
||||||
manga.description = infoElement.select("p:has(span:contains(Summary:)) ~ p").text()
|
|
||||||
manga.status = infoElement.select("p:has(span:contains(Status:))").first()?.text().orEmpty().let { parseStatus(it) }
|
|
||||||
manga.thumbnail_url = document.select(".rightBox:eq(0) img").first()?.attr("src")
|
|
||||||
return manga
|
|
||||||
}
|
|
||||||
|
|
||||||
fun parseStatus(status: String) = when {
|
|
||||||
status.contains("Ongoing") -> SManga.ONGOING
|
|
||||||
status.contains("Completed") -> SManga.COMPLETED
|
|
||||||
else -> SManga.UNKNOWN
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun chapterListSelector() = "table.listing tr:gt(1)"
|
|
||||||
|
|
||||||
override fun chapterFromElement(element: Element): SChapter {
|
|
||||||
val urlElement = element.select("a").first()
|
|
||||||
|
|
||||||
val chapter = SChapter.create()
|
|
||||||
chapter.setUrlWithoutDomain(urlElement.attr("href"))
|
|
||||||
chapter.name = urlElement.text()
|
|
||||||
chapter.date_upload = element.select("td:eq(1)").first()?.text()?.let {
|
|
||||||
SimpleDateFormat("MM/dd/yyyy").parse(it).time
|
|
||||||
} ?: 0
|
|
||||||
return chapter
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun pageListRequest(chapter: SChapter) = POST(baseUrl + chapter.url, headers)
|
|
||||||
|
|
||||||
override fun pageListParse(response: Response): List<Page> {
|
|
||||||
val body = response.body()!!.string()
|
|
||||||
|
|
||||||
val pages = mutableListOf<Page>()
|
|
||||||
|
|
||||||
// Kissmanga now encrypts the urls, so we need to execute these two scripts in JS.
|
|
||||||
val ca = client.newCall(GET("$baseUrl/Scripts/ca.js", headers)).execute().body()!!.string()
|
|
||||||
val lo = client.newCall(GET("$baseUrl/Scripts/lo.js", headers)).execute().body()!!.string()
|
|
||||||
|
|
||||||
Duktape.create().use {
|
|
||||||
it.evaluate(ca)
|
|
||||||
it.evaluate(lo)
|
|
||||||
|
|
||||||
// There are two functions in an inline script needed to decrypt the urls. We find and
|
|
||||||
// execute them.
|
|
||||||
var p = Pattern.compile("(var.*CryptoJS.*)")
|
|
||||||
var m = p.matcher(body)
|
|
||||||
while (m.find()) {
|
|
||||||
it.evaluate(m.group(1))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finally find all the urls and decrypt them in JS.
|
|
||||||
p = Pattern.compile("""lst[A-Z]+.push\((.*)\);""")
|
|
||||||
m = p.matcher(body)
|
|
||||||
|
|
||||||
var i = 0
|
|
||||||
while (m.find()) {
|
|
||||||
val url = it.evaluate(m.group(1)) as String
|
|
||||||
pages.add(Page(i++, "", url))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return pages
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun pageListParse(document: Document): List<Page> {
|
|
||||||
throw Exception("Not used")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun imageUrlRequest(page: Page) = GET(page.url)
|
|
||||||
|
|
||||||
override fun imageUrlParse(document: Document) = ""
|
|
||||||
|
|
||||||
private class Status : Filter.TriState("Completed")
|
|
||||||
private class Author : Filter.Text("Author")
|
|
||||||
private class Genre(name: String) : Filter.TriState(name)
|
|
||||||
private class GenreList(genres: List<Genre>) : Filter.Group<Genre>("Genres", genres)
|
|
||||||
private class SortTrending : Filter.CheckBox("View Trending Manga")
|
|
||||||
private class NewManga : Filter.CheckBox("View New Manga")
|
|
||||||
|
|
||||||
override fun getFilterList() = FilterList(
|
|
||||||
Author(),
|
|
||||||
Status(),
|
|
||||||
GenreList(getGenreList()),
|
|
||||||
Filter.Separator(),
|
|
||||||
Filter.Header("Change Manga List"),
|
|
||||||
SortTrending(),
|
|
||||||
NewManga()
|
|
||||||
)
|
|
||||||
|
|
||||||
// $("select[name=\"genres\"]").map((i,el) => `Genre("${$(el).next().text().trim()}", ${i})`).get().join(',\n')
|
|
||||||
// on https://kissmanga.com/AdvanceSearch
|
|
||||||
private fun getGenreList() = listOf(
|
|
||||||
Genre("4-Koma"),
|
|
||||||
Genre("Action"),
|
|
||||||
Genre("Adult"),
|
|
||||||
Genre("Adventure"),
|
|
||||||
Genre("Comedy"),
|
|
||||||
Genre("Comic"),
|
|
||||||
Genre("Cooking"),
|
|
||||||
Genre("Doujinshi"),
|
|
||||||
Genre("Drama"),
|
|
||||||
Genre("Ecchi"),
|
|
||||||
Genre("Fantasy"),
|
|
||||||
Genre("Gender Bender"),
|
|
||||||
Genre("Harem"),
|
|
||||||
Genre("Historical"),
|
|
||||||
Genre("Horror"),
|
|
||||||
Genre("Isekai"),
|
|
||||||
Genre("Josei"),
|
|
||||||
Genre("Lolicon"),
|
|
||||||
Genre("Manga"),
|
|
||||||
Genre("Manhua"),
|
|
||||||
Genre("Manhwa"),
|
|
||||||
Genre("Martial Arts"),
|
|
||||||
Genre("Mature"),
|
|
||||||
Genre("Mecha"),
|
|
||||||
Genre("Medical"),
|
|
||||||
Genre("Music"),
|
|
||||||
Genre("Mystery"),
|
|
||||||
Genre("One shot"),
|
|
||||||
Genre("Psychological"),
|
|
||||||
Genre("Romance"),
|
|
||||||
Genre("School Life"),
|
|
||||||
Genre("Sci-fi"),
|
|
||||||
Genre("Seinen"),
|
|
||||||
Genre("Shotacon"),
|
|
||||||
Genre("Shoujo"),
|
|
||||||
Genre("Shoujo Ai"),
|
|
||||||
Genre("Shounen"),
|
|
||||||
Genre("Shounen Ai"),
|
|
||||||
Genre("Slice of Life"),
|
|
||||||
Genre("Smut"),
|
|
||||||
Genre("Sports"),
|
|
||||||
Genre("Supernatural"),
|
|
||||||
Genre("Tragedy"),
|
|
||||||
Genre("Webtoon"),
|
|
||||||
Genre("Yaoi"),
|
|
||||||
Genre("Yuri")
|
|
||||||
)
|
|
||||||
}
|
|
Loading…
Reference in New Issue