Port E-Hentai from ilwaz/TachiyomiEH (closes #960)

This commit is contained in:
Eugene Cheung 2019-03-28 23:00:11 -04:00 committed by Eugene
parent 3c41ee7dea
commit 3014f5b3f6
3 changed files with 94 additions and 63 deletions

View File

@ -5,7 +5,7 @@ ext {
appName = 'Tachiyomi: E-Hentai' appName = 'Tachiyomi: E-Hentai'
pkgNameSuffix = 'all.ehentai' pkgNameSuffix = 'all.ehentai'
extClass = '.EHJapanese; .EHEnglish; .EHChinese; .EHDutch; .EHFrench; .EHGerman; .EHHungarian; .EHItalian; .EHKorean; .EHPolish; .EHPolish; .EHPortuguese; .EHRussian; .EHSpanish; .EHThai; .EHVietnamese; .EHSpeechless; .EHOther' extClass = '.EHJapanese; .EHEnglish; .EHChinese; .EHDutch; .EHFrench; .EHGerman; .EHHungarian; .EHItalian; .EHKorean; .EHPolish; .EHPolish; .EHPortuguese; .EHRussian; .EHSpanish; .EHThai; .EHVietnamese; .EHSpeechless; .EHOther'
extVersionCode = 2 extVersionCode = 3
libVersion = '1.2' libVersion = '1.2'
} }

View File

@ -3,14 +3,19 @@ package eu.kanade.tachiyomi.extension.all.ehentai
import android.net.Uri import android.net.Uri
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.source.model.* import eu.kanade.tachiyomi.source.model.Filter
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.HttpSource import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.CacheControl import okhttp3.CacheControl
import okhttp3.CookieJar
import okhttp3.Headers import okhttp3.Headers
import okhttp3.Request import okhttp3.Request
import okhttp3.Response import okhttp3.Response
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
import rx.Observable import rx.Observable
import java.net.URLEncoder import java.net.URLEncoder
@ -23,47 +28,37 @@ open class EHentai(override val lang: String, val ehLang: String) : HttpSource()
override val supportsLatest = true override val supportsLatest = true
/**
* Gallery list entry
* @param fav The favorite this gallery belongs to (currently unused)
* @param manga The manga object
*/
data class ParsedManga(val fav: String?, val manga: SManga)
fun extendedGenericMangaParse(doc: Document) val initMetaRegex = """inits?~(?:ul\.)?(.*?)~(.*?)~""".toRegex()
= with(doc) {
//Parse mangas fun parseInitsMeta(meta: String): String{
val parsedMangas = select(".gtr0,.gtr1").map { val match = initMetaRegex.find(meta)
ParsedManga( return "https://" + match?.groupValues?.get(1) +"/"+ match?.groupValues?.get(2)
fav = it.select(".itd .it3 > .i[id]").attr("title"), }
manga = SManga.create().apply {
private fun genericMangaParse(response: Response): MangasPage {
val doc = response.asJsoup()
val parsedMangas = doc.select("table.itg td.glname").map {
SManga.create().apply {
//Get title //Get title
it.select(".itd .it5 a").apply { it.select("a")?.first()?.apply {
title = text() title = text()
setUrlWithoutDomain(addParam(attr("href"), "nw", "always")) url = ExGalleryMetadata.normalizeUrl(attr("href"))
} }
//Get image //Get image
it.select(".itd .it2").first().apply { it.parent().select(".glthumb")?.first().apply {
children().first()?.let { thumbnail_url = this!!.select("img").first()?.attr("src")?.nullIfBlank()
thumbnail_url = it.attr("src") ?: parseInitsMeta(it.parent()
} ?: text().split("~").apply { .select(".glthumb").first()
thumbnail_url = "http://${this[1]}/${this[2]}" .childNode(0).toString())
}
} }
} }
})
}
//Add to page if required //Add to page if required
val hasNextPage = select("a[onclick=return false]").last()?.text() == ">" val hasNextPage = doc.select("a[onclick=return false]").last()?.text() == ">"
Pair(parsedMangas, hasNextPage)
}
/** return MangasPage(parsedMangas, hasNextPage)
* Parse a list of galleries
*/
fun genericMangaParse(response: Response)
= extendedGenericMangaParse(response.asJsoup()).let {
MangasPage(it.first.map { it.manga }, it.second)
} }
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> override fun fetchChapterList(manga: SManga): Observable<List<SChapter>>
@ -85,7 +80,7 @@ open class EHentai(override val lang: String, val ehLang: String) : HttpSource()
*/ */
private fun fetchChapterPage(chapter: SChapter, np: String, private fun fetchChapterPage(chapter: SChapter, np: String,
pastUrls: List<String> = emptyList()): Observable<List<String>> { pastUrls: List<String> = emptyList()): Observable<List<String>> {
val urls = pastUrls.toMutableList() val urls = ArrayList(pastUrls)
return chapterPageCall(np).flatMap { return chapterPageCall(np).flatMap {
val jsoup = it.asJsoup() val jsoup = it.asJsoup()
urls += parseChapterPage(jsoup) urls += parseChapterPage(jsoup)
@ -162,15 +157,22 @@ open class EHentai(override val lang: String, val ehLang: String) : HttpSource()
thumbnailUrl = select("#gd1 div").attr("style").nullIfBlank()?.let { thumbnailUrl = select("#gd1 div").attr("style").nullIfBlank()?.let {
it.substring(it.indexOf('(') + 1 until it.lastIndexOf(')')) it.substring(it.indexOf('(') + 1 until it.lastIndexOf(')'))
} }
genre = select("#gdc div").text().nullIfBlank()?.trim()?.toLowerCase()
genre = select(".ic").parents().attr("href").nullIfBlank()?.trim()?.substringAfterLast('/')
uploader = select("#gdn").text().nullIfBlank()?.trim() uploader = select("#gdn").text().nullIfBlank()?.trim()
//Parse the table //Parse the table
select("#gdd tr").forEach { select("#gdd tr").forEach {
val left = it.select(".gdt1").text().nullIfBlank()?.trim() ?: return@forEach it.select(".gdt1")
val right = it.select(".gdt2").text().nullIfBlank()?.trim() ?: return@forEach .text()
.nullIfBlank()
?.trim()
?.let { left ->
it.select(".gdt2")
.text()
.nullIfBlank()
?.trim()
?.let { right ->
ignore { ignore {
when (left.removeSuffix(":") when (left.removeSuffix(":")
.toLowerCase()) { .toLowerCase()) {
@ -186,16 +188,18 @@ open class EHentai(override val lang: String, val ehLang: String) : HttpSource()
} }
} }
} }
}
}
//Parse ratings //Parse ratings
ignore { ignore {
averageRating = getElementById("rating_label") averageRating = select("#rating_label")
.text() .text()
.removePrefix("Average:") .removePrefix("Average:")
.trim() .trim()
.nullIfBlank() .nullIfBlank()
?.toDouble() ?.toDouble()
ratingCount = getElementById("rating_count") ratingCount = select("#rating_count")
.text() .text()
.trim() .trim()
.nullIfBlank() .nullIfBlank()
@ -237,7 +241,7 @@ open class EHentai(override val lang: String, val ehLang: String) : HttpSource()
//TODO We cannot currently do this as page.url is immutable //TODO We cannot currently do this as page.url is immutable
//Each press of the retry button will choose another server //Each press of the retry button will choose another server
/*select("#loadfail").attr("onclick").nullIfBlank()?.let { /*select("#loadfail").attr("onclick").nullIfBlank()?.let {
page.url = addParam(page.url, "nl", it.substring(it.indexOf('\'') + 1 .. it.lastIndexOf('\'') - 1)) page.url = addParam(page.url, "nl", it.substring(it.indexOf('\'') + 1 until it.lastIndexOf('\'')))
}*/ }*/
currentImage currentImage
}!! }!!
@ -283,10 +287,12 @@ open class EHentai(override val lang: String, val ehLang: String) : HttpSource()
.toString() .toString()
override val client = network.client.newBuilder() override val client = network.client.newBuilder()
.addNetworkInterceptor { chain -> .cookieJar(CookieJar.NO_COOKIES)
.addInterceptor { chain ->
val newReq = chain val newReq = chain
.request() .request()
.newBuilder() .newBuilder()
.removeHeader("Cookie")
.addHeader("Cookie", cookiesHeader) .addHeader("Cookie", cookiesHeader)
.build() .build()

View File

@ -1,5 +1,7 @@
package eu.kanade.tachiyomi.extension.all.ehentai; package eu.kanade.tachiyomi.extension.all.ehentai;
import android.net.Uri
/** /**
* Gallery metadata storage model * Gallery metadata storage model
*/ */
@ -28,4 +30,27 @@ class ExGalleryMetadata {
var uploader: String? = null var uploader: String? = null
val tags: MutableMap<String, List<Tag>> = mutableMapOf() val tags: MutableMap<String, List<Tag>> = mutableMapOf()
companion object {
private fun splitGalleryUrl(url: String)
= url.let {
//Only parse URL if is full URL
val pathSegments = if(it.startsWith("http"))
Uri.parse(it).pathSegments
else
it.split('/')
pathSegments.filterNot(String::isNullOrBlank)
}
fun galleryId(url: String) = splitGalleryUrl(url)[1]
fun galleryToken(url: String) =
splitGalleryUrl(url)[2]
fun normalizeUrl(id: String, token: String)
= "/g/$id/$token/?nw=always"
fun normalizeUrl(url: String)
= normalizeUrl(galleryId(url), galleryToken(url))
}
} }