Added HentaiMimi (#6890)

* Create AndroidManifest.xml

* Added HentaiMimi

* Added Icons
This commit is contained in:
Johannes Joens 2021-05-08 13:33:02 +12:00 committed by GitHub
parent a66da6bd06
commit a235527858
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 1724 additions and 0 deletions

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="eu.kanade.tachiyomi.extension" />

View File

@ -0,0 +1,13 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
ext {
extName = 'HentaiMimi'
pkgNameSuffix = 'en.hentaimimi'
extClass = '.HentaiMimi'
extVersionCode = 1
libVersion = '1.2'
containsNsfw = true
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

View File

@ -0,0 +1,147 @@
package eu.kanade.tachiyomi.extension.en.hentaimimi
import android.annotation.SuppressLint
import android.annotation.TargetApi
import android.os.Build
import okhttp3.OkHttpClient
import okhttp3.Request
import org.jsoup.Jsoup
import org.jsoup.nodes.Document
import java.io.PrintWriter
import java.security.cert.CertificateException
import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter
import java.util.concurrent.TimeUnit
import javax.net.ssl.SSLContext
import javax.net.ssl.TrustManager
import javax.net.ssl.X509TrustManager
/*
* Based on the one in MMRCMS extension
*/
class FilterOptGen {
init {
System.setProperty("https.protocols", "TLSv1,TLSv1.1,TLSv1.2,TLSv1.3")
}
val types = listOf("artists", "parodies", "langs", "pubs", "tags")
@TargetApi(Build.VERSION_CODES.O)
fun generate() {
val buffer = StringBuffer()
val dateTime = ZonedDateTime.now()
val formattedDate = dateTime.format(DateTimeFormatter.RFC_1123_DATE_TIME)
buffer.append("package eu.kanade.tachiyomi.extension.en.hentaimimi")
buffer.append("\n\n// GENERATED FILE, DO NOT MODIFY!\n// Generated $formattedDate\n\n")
var number = 1
types.forEach {
try {
val document = getDocument("https://hentaimimi.com/search")
val ids = parseIds(document!!, it)
buffer.append("fun $it() = listOf(\n")
for (id in ids) {
when (it) {
"tags" -> buffer.append(" HentaiMimi.TriStateFilterOption(\"${id.first}\",\"${id.second}\"),\n")
else -> buffer.append(" HentaiMimi.CheckboxFilterOption(\"${id.first}\",\"${id.second}\"),\n")
}
}
buffer.append(")\n\n")
number++
} catch (e: Exception) {
println("error generating source $it ${e.printStackTrace()}")
}
}
println("Post-run types: ${number - 1}")
val writer = PrintWriter(relativePath)
writer.write(buffer.toString())
writer.close()
}
private fun getDocument(url: String): Document? {
val serverCheck = arrayOf("cloudflare-nginx", "cloudflare")
try {
val request = Request.Builder().url(url)
getOkHttpClient().newCall(request.build()).execute().let { response ->
// Bypass Cloudflare ("Please wait 5 seconds" page)
if (response.code == 503 && response.header("Server") in serverCheck) {
var cookie = "${response.header("Set-Cookie")!!.substringBefore(";")}; "
Jsoup.parse(response.body!!.string()).let { document ->
val path = document.select("[id=\"challenge-form\"]").attr("action")
val chk = document.select("[name=\"s\"]").attr("value")
getOkHttpClient().newCall(Request.Builder().url("$url/$path?s=$chk").build()).execute().let { solved ->
cookie += solved.header("Set-Cookie")!!.substringBefore(";")
request.addHeader("Cookie", cookie).build().let {
return Jsoup.parse(getOkHttpClient().newCall(it).execute().body?.string())
}
}
}
}
if (response.code == 200) {
return Jsoup.parse(response.body?.string())
}
}
} catch (e: Exception) {
e.printStackTrace()
}
return null
}
private fun parseIds(Document: Document, type: String): List<Pair<String, String>> {
val elements = Document.select("[name=\"$type${"[]"}\"] > option")
val ids = mutableListOf<Pair<String, String>>()
if (elements.isEmpty()) {
return ids
}
elements.forEach {
ids.add(Pair(it.text(), it.attr("value")))
}
return ids
}
@Throws(Exception::class)
private fun getOkHttpClient(): OkHttpClient {
val trustAllCerts = arrayOf<TrustManager>(
object : X509TrustManager {
@SuppressLint("TrustAllX509TrustManager")
@Throws(CertificateException::class)
override fun checkClientTrusted(chain: Array<java.security.cert.X509Certificate>, authType: String) {
}
@SuppressLint("TrustAllX509TrustManager")
@Throws(CertificateException::class)
override fun checkServerTrusted(chain: Array<java.security.cert.X509Certificate>, authType: String) {
}
override fun getAcceptedIssuers(): Array<java.security.cert.X509Certificate> {
return arrayOf()
}
}
)
// Install the all-trusting trust manager
val sc = SSLContext.getInstance("SSL")
sc.init(null, trustAllCerts, java.security.SecureRandom())
val sslSocketFactory = sc.socketFactory
// Create all-trusting host name verifier
// Install the all-trusting host verifier
return OkHttpClient.Builder()
.sslSocketFactory(sslSocketFactory, trustAllCerts[0] as X509TrustManager)
.hostnameVerifier { _, _ -> true }
.connectTimeout(1, TimeUnit.MINUTES)
.readTimeout(1, TimeUnit.MINUTES)
.writeTimeout(1, TimeUnit.MINUTES)
.build()
}
companion object {
val relativePath = System.getProperty("user.dir") + "/src/en/hentaimimi/src/eu/kanade/tachiyomi/extension/en/hentaimimi/HentaiMimiIDs.kt"
@JvmStatic
fun main(args: Array<String>) {
FilterOptGen().generate()
}
}
}

View File

@ -0,0 +1,177 @@
package eu.kanade.tachiyomi.extension.en.hentaimimi
import eu.kanade.tachiyomi.annotations.Nsfw
import eu.kanade.tachiyomi.network.GET
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 okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.Request
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import rx.Observable
/*
* To Update Filter Values Run "FilterOptGen.kt"
*
*
*/
@Nsfw
class HentaiMimi : ParsedHttpSource() {
// Meta Data
override val baseUrl = "https://hentaimimi.com"
override val lang = "en"
override val name = "HentaiMimi"
override val supportsLatest = true
// Latest
override fun latestUpdatesFromElement(element: Element): SManga {
val manga = SManga.create()
val title = element.select(".white-text")
manga.url = title.attr("abs:href").replace(baseUrl, "").trim()
manga.title = title.text()
manga.thumbnail_url = element.select(".card-img-top").attr("abs:src")
return manga
}
override fun latestUpdatesNextPageSelector(): String? = ":not(*)"
override fun latestUpdatesRequest(page: Int): Request {
return GET(baseUrl, headers)
}
override fun latestUpdatesSelector(): String = "div.row:nth-child(2) > div"
// Popular
override fun popularMangaFromElement(element: Element): SManga = latestUpdatesFromElement(element)
override fun popularMangaNextPageSelector(): String? = "li.page-item.active + li.page-item"
override fun popularMangaRequest(page: Int): Request {
return GET("$baseUrl/index?page=$page", headers)
}
override fun popularMangaSelector(): String = "div.row:nth-child(4) > div"
// Search
override fun searchMangaFromElement(element: Element): SManga = latestUpdatesFromElement(element)
override fun searchMangaNextPageSelector(): String? = popularMangaNextPageSelector()
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
val url = "$baseUrl/search".toHttpUrlOrNull()?.newBuilder()!!
if (query.isNotEmpty()) url.addQueryParameter("title", query)
filters.forEach { filter ->
when (filter) {
is ArtistGroupFilter -> {
filter.state.filter { it.state }.map { it.value }.forEach { url.addQueryParameter("artists[]", it) }
}
is ParodiesGroupFilter -> {
filter.state.filter { it.state }.map { it.value }.forEach { url.addQueryParameter("parodies[]", it) }
}
is LanguageGroupFilter -> {
filter.state.filter { it.state }.map { it.value }.forEach { url.addQueryParameter("langs[]", it) }
}
is PublishersGroupFilter -> {
filter.state.filter { it.state }.map { it.value }.forEach { url.addQueryParameter("pubs[]", it) }
}
is TagGroupFilter -> {
filter.state.filter { it.isExcluded() }.map { it.value }.forEach { url.addQueryParameter("tags_ex[]", it) }
filter.state.filter { it.isIncluded() }.map { it.value }.forEach { url.addQueryParameter("tags[]", it) }
}
}
}
url.addQueryParameter("page", page.toString())
return GET(url.toString(), headers)
}
override fun searchMangaSelector(): String = "div.row:nth-child(2) > div"
// Mangas
override fun mangaDetailsParse(document: Document): SManga {
val manga = SManga.create()
val details = document.select("div.p-0")
val tags = mutableListOf<String>()
manga.title = details.select("h3").text()
manga.url = document.location().replace(baseUrl, "").trim()
manga.thumbnail_url = document.select("div.col-md-4 img").attr("abs:src")
details.select(".mb-3").forEach {
when (it.select(".lead").text()) {
"Artist" -> {
manga.artist = it.select("p:nth-child(2)").text()
manga.author = manga.artist
}
"Description" -> manga.description = it.select("p:nth-child(2)").text()
"Tags" -> it.select("p:nth-child(2) > a > span").forEach { tag ->
tags.add(tag.text())
}
"Language" -> tags.add(it.select("p:nth-child(2)").text())
"Magazine" -> tags.add(it.select("p:nth-child(2)").text())
"Publisher" -> tags.add(it.select("p:nth-child(2)").text())
}
}
manga.status = 2
manga.genre = tags.joinToString(", ")
return manga
}
// Chapters
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> {
return Observable.just(
listOf(
SChapter.create().apply {
name = "Chapter"
url = manga.url
chapter_number = 1F
date_upload = System.currentTimeMillis()
}
)
)
}
override fun chapterFromElement(element: Element): SChapter = throw UnsupportedOperationException("Not used")
override fun chapterListSelector(): String = throw UnsupportedOperationException("Not used")
override fun imageUrlParse(document: Document): String = throw UnsupportedOperationException("Not used")
override fun pageListParse(document: Document): List<Page> {
val pages = mutableListOf<Page>()
document.select("body main script").html().substringAfter("[").substringBefore("]").split(",").forEachIndexed { index, it ->
val url = "$baseUrl/${it.replace("\\", "").replace("\"", "")}"
pages.add(Page(index, url, url))
}
/*document.select("div#lightgallery > a").forEachIndexed() { index, it ->
val url = it.select("img").attr("abs:src")
pages.add(Page(index, url, url))
}*/
return pages
}
// Filters
class TriStateFilterOption(name: String, val value: String) : Filter.TriState(name)
class CheckboxFilterOption(name: String, val value: String, default: Boolean = false) : Filter.CheckBox(name, default)
private class TagGroupFilter(filters: List<TriStateFilterOption>) : Filter.Group<TriStateFilterOption>("Tags", filters)
private class LanguageGroupFilter(options: List<CheckboxFilterOption>) : Filter.Group<CheckboxFilterOption>("Languages", options)
private class ArtistGroupFilter(options: List<CheckboxFilterOption>) : Filter.Group<CheckboxFilterOption>("Artist", options)
private class ParodiesGroupFilter(options: List<CheckboxFilterOption>) : Filter.Group<CheckboxFilterOption>("Parodies", options)
private class PublishersGroupFilter(options: List<CheckboxFilterOption>) : Filter.Group<CheckboxFilterOption>("Publishers", options)
override fun getFilterList(): FilterList = FilterList(
ArtistGroupFilter(artists()),
ParodiesGroupFilter(parodies()),
LanguageGroupFilter(langs()),
PublishersGroupFilter(pubs()),
TagGroupFilter(tags()),
)
}