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'
pkgNameSuffix = 'all.ehentai'
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'
}

View File

@ -3,14 +3,19 @@ package eu.kanade.tachiyomi.extension.all.ehentai
import android.net.Uri
import eu.kanade.tachiyomi.network.GET
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.util.asJsoup
import okhttp3.CacheControl
import okhttp3.CookieJar
import okhttp3.Headers
import okhttp3.Request
import okhttp3.Response
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import rx.Observable
import java.net.URLEncoder
@ -23,47 +28,37 @@ open class EHentai(override val lang: String, val ehLang: String) : HttpSource()
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)
= with(doc) {
//Parse mangas
val parsedMangas = select(".gtr0,.gtr1").map {
ParsedManga(
fav = it.select(".itd .it3 > .i[id]").attr("title"),
manga = SManga.create().apply {
//Get title
it.select(".itd .it5 a").apply {
title = text()
setUrlWithoutDomain(addParam(attr("href"), "nw", "always"))
}
//Get image
it.select(".itd .it2").first().apply {
children().first()?.let {
thumbnail_url = it.attr("src")
} ?: text().split("~").apply {
thumbnail_url = "http://${this[1]}/${this[2]}"
}
}
})
val initMetaRegex = """inits?~(?:ul\.)?(.*?)~(.*?)~""".toRegex()
}
//Add to page if required
val hasNextPage = select("a[onclick=return false]").last()?.text() == ">"
Pair(parsedMangas, hasNextPage)
fun parseInitsMeta(meta: String): String{
val match = initMetaRegex.find(meta)
return "https://" + match?.groupValues?.get(1) +"/"+ match?.groupValues?.get(2)
}
/**
* Parse a list of galleries
*/
fun genericMangaParse(response: Response)
= extendedGenericMangaParse(response.asJsoup()).let {
MangasPage(it.first.map { it.manga }, it.second)
private fun genericMangaParse(response: Response): MangasPage {
val doc = response.asJsoup()
val parsedMangas = doc.select("table.itg td.glname").map {
SManga.create().apply {
//Get title
it.select("a")?.first()?.apply {
title = text()
url = ExGalleryMetadata.normalizeUrl(attr("href"))
}
//Get image
it.parent().select(".glthumb")?.first().apply {
thumbnail_url = this!!.select("img").first()?.attr("src")?.nullIfBlank()
?: parseInitsMeta(it.parent()
.select(".glthumb").first()
.childNode(0).toString())
}
}
}
//Add to page if required
val hasNextPage = doc.select("a[onclick=return false]").last()?.text() == ">"
return MangasPage(parsedMangas, hasNextPage)
}
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,
pastUrls: List<String> = emptyList()): Observable<List<String>> {
val urls = pastUrls.toMutableList()
val urls = ArrayList(pastUrls)
return chapterPageCall(np).flatMap {
val jsoup = it.asJsoup()
urls += parseChapterPage(jsoup)
@ -162,40 +157,49 @@ open class EHentai(override val lang: String, val ehLang: String) : HttpSource()
thumbnailUrl = select("#gd1 div").attr("style").nullIfBlank()?.let {
it.substring(it.indexOf('(') + 1 until it.lastIndexOf(')'))
}
genre = select(".ic").parents().attr("href").nullIfBlank()?.trim()?.substringAfterLast('/')
genre = select("#gdc div").text().nullIfBlank()?.trim()?.toLowerCase()
uploader = select("#gdn").text().nullIfBlank()?.trim()
//Parse the table
select("#gdd tr").forEach {
val left = it.select(".gdt1").text().nullIfBlank()?.trim() ?: return@forEach
val right = it.select(".gdt2").text().nullIfBlank()?.trim() ?: return@forEach
ignore {
when (left.removeSuffix(":")
.toLowerCase()) {
"posted" -> datePosted = EX_DATE_FORMAT.parse(right).time
"visible" -> visible = right.nullIfBlank()
"language" -> {
language = right.removeSuffix(TR_SUFFIX).trim().nullIfBlank()
translated = right.endsWith(TR_SUFFIX, true)
it.select(".gdt1")
.text()
.nullIfBlank()
?.trim()
?.let { left ->
it.select(".gdt2")
.text()
.nullIfBlank()
?.trim()
?.let { right ->
ignore {
when (left.removeSuffix(":")
.toLowerCase()) {
"posted" -> datePosted = EX_DATE_FORMAT.parse(right).time
"visible" -> visible = right.nullIfBlank()
"language" -> {
language = right.removeSuffix(TR_SUFFIX).trim().nullIfBlank()
translated = right.endsWith(TR_SUFFIX, true)
}
"file size" -> size = parseHumanReadableByteCount(right)?.toLong()
"length" -> length = right.removeSuffix("pages").trim().nullIfBlank()?.toInt()
"favorited" -> favorites = right.removeSuffix("times").trim().nullIfBlank()?.toInt()
}
}
}
}
"file size" -> size = parseHumanReadableByteCount(right)?.toLong()
"length" -> length = right.removeSuffix("pages").trim().nullIfBlank()?.toInt()
"favorited" -> favorites = right.removeSuffix("times").trim().nullIfBlank()?.toInt()
}
}
}
//Parse ratings
ignore {
averageRating = getElementById("rating_label")
averageRating = select("#rating_label")
.text()
.removePrefix("Average:")
.trim()
.nullIfBlank()
?.toDouble()
ratingCount = getElementById("rating_count")
ratingCount = select("#rating_count")
.text()
.trim()
.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
//Each press of the retry button will choose another server
/*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
}!!
@ -283,10 +287,12 @@ open class EHentai(override val lang: String, val ehLang: String) : HttpSource()
.toString()
override val client = network.client.newBuilder()
.addNetworkInterceptor { chain ->
.cookieJar(CookieJar.NO_COOKIES)
.addInterceptor { chain ->
val newReq = chain
.request()
.newBuilder()
.removeHeader("Cookie")
.addHeader("Cookie", cookiesHeader)
.build()

View File

@ -1,5 +1,7 @@
package eu.kanade.tachiyomi.extension.all.ehentai;
import android.net.Uri
/**
* Gallery metadata storage model
*/
@ -28,4 +30,27 @@ class ExGalleryMetadata {
var uploader: String? = null
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))
}
}