mirror of
https://github.com/keiyoushi/extensions-source.git
synced 2024-11-25 11:42:47 +01:00
NHentai | Fixed some images not showing for some titles (#6070)
* NHentai | Fixed some images not showing for some titles * little * Apply AwkwardPeak's suggestions * comma
This commit is contained in:
parent
54578a5282
commit
bc7e462516
@ -1,7 +1,7 @@
|
||||
ext {
|
||||
extName = 'NHentai'
|
||||
extClass = '.NHFactory'
|
||||
extVersionCode = 46
|
||||
extVersionCode = 47
|
||||
isNsfw = true
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,37 @@
|
||||
package eu.kanade.tachiyomi.extension.all.nhentai
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
class Hentai(
|
||||
var id: Int,
|
||||
val images: Images,
|
||||
val media_id: String,
|
||||
val tags: List<Tag>,
|
||||
val title: Title,
|
||||
val upload_date: Long,
|
||||
val num_favorites: Long,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
class Title(
|
||||
var english: String? = null,
|
||||
val japanese: String? = null,
|
||||
val pretty: String? = null,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
class Images(
|
||||
val pages: List<Image>,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
class Image(
|
||||
val t: String,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
class Tag(
|
||||
val name: String,
|
||||
val type: String,
|
||||
)
|
@ -1,63 +1,36 @@
|
||||
package eu.kanade.tachiyomi.extension.all.nhentai
|
||||
|
||||
import org.jsoup.nodes.Document
|
||||
import org.jsoup.nodes.Element
|
||||
import java.text.SimpleDateFormat
|
||||
|
||||
object NHUtils {
|
||||
fun getArtists(document: Document): String {
|
||||
val artists = document.select("#tags > div:nth-child(4) > span > a .name")
|
||||
return artists.joinToString(", ") { it.cleanTag() }
|
||||
fun getArtists(data: Hentai): String {
|
||||
val artists = data.tags.filter { it.type == "artist" }
|
||||
return artists.joinToString(", ") { it.name }
|
||||
}
|
||||
|
||||
fun getGroups(document: Document): String? {
|
||||
val groups = document.select("#tags > div:nth-child(5) > span > a .name")
|
||||
return if (groups.isNotEmpty()) {
|
||||
groups.joinToString(", ") { it.cleanTag() }
|
||||
} else {
|
||||
null
|
||||
fun getGroups(data: Hentai): String? {
|
||||
val groups = data.tags.filter { it.type == "group" }
|
||||
return groups.joinToString(", ") { it.name }.takeIf { it.isBlank() }
|
||||
}
|
||||
|
||||
fun getTagDescription(data: Hentai): String {
|
||||
val tags = data.tags.groupBy { it.type }
|
||||
return buildString {
|
||||
tags["category"]?.joinToString { it.name }?.let {
|
||||
append("Categories: ", it, "\n")
|
||||
}
|
||||
tags["parody"]?.joinToString { it.name }?.let {
|
||||
append("Parodies: ", it, "\n")
|
||||
}
|
||||
tags["character"]?.joinToString { it.name }?.let {
|
||||
append("Characters: ", it, "\n\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getTagDescription(document: Document): String {
|
||||
val stringBuilder = StringBuilder()
|
||||
|
||||
val categories = document.select("#tags > div:nth-child(7) > span > a .name")
|
||||
if (categories.isNotEmpty()) {
|
||||
stringBuilder.append("Categories: ")
|
||||
stringBuilder.append(categories.joinToString(", ") { it.cleanTag() })
|
||||
stringBuilder.append("\n\n")
|
||||
}
|
||||
|
||||
val parodies = document.select("#tags > div:nth-child(1) > span > a .name")
|
||||
if (parodies.isNotEmpty()) {
|
||||
stringBuilder.append("Parodies: ")
|
||||
stringBuilder.append(parodies.joinToString(", ") { it.cleanTag() })
|
||||
stringBuilder.append("\n\n")
|
||||
}
|
||||
|
||||
val characters = document.select("#tags > div:nth-child(2) > span > a .name")
|
||||
if (characters.isNotEmpty()) {
|
||||
stringBuilder.append("Characters: ")
|
||||
stringBuilder.append(characters.joinToString(", ") { it.cleanTag() })
|
||||
}
|
||||
|
||||
return stringBuilder.toString()
|
||||
}
|
||||
|
||||
fun getTags(document: Document): String {
|
||||
val tags = document.select("#tags > div:nth-child(3) > span > a .name")
|
||||
return tags.map { it.cleanTag() }.sorted().joinToString(", ")
|
||||
}
|
||||
|
||||
fun getNumPages(document: Document): String {
|
||||
return document.select("#tags > div:nth-child(8) > span > a .name").first()!!.cleanTag()
|
||||
}
|
||||
|
||||
fun getTime(document: Document): Long {
|
||||
val timeString = document.toString().substringAfter("datetime=\"").substringBefore("\">").replace("T", " ")
|
||||
|
||||
return SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSSSSZ").parse(timeString)?.time ?: 0L
|
||||
fun getTags(data: Hentai): String {
|
||||
val artists = data.tags.filter { it.type == "tag" }
|
||||
return artists.joinToString(", ") { it.name }
|
||||
}
|
||||
|
||||
private fun Element.cleanTag(): String = text().replace(Regex("\\(.*\\)"), "").trim()
|
||||
|
@ -6,10 +6,8 @@ import androidx.preference.ListPreference
|
||||
import androidx.preference.PreferenceScreen
|
||||
import eu.kanade.tachiyomi.extension.all.nhentai.NHUtils.getArtists
|
||||
import eu.kanade.tachiyomi.extension.all.nhentai.NHUtils.getGroups
|
||||
import eu.kanade.tachiyomi.extension.all.nhentai.NHUtils.getNumPages
|
||||
import eu.kanade.tachiyomi.extension.all.nhentai.NHUtils.getTagDescription
|
||||
import eu.kanade.tachiyomi.extension.all.nhentai.NHUtils.getTags
|
||||
import eu.kanade.tachiyomi.extension.all.nhentai.NHUtils.getTime
|
||||
import eu.kanade.tachiyomi.lib.randomua.addRandomUAPreferenceToScreen
|
||||
import eu.kanade.tachiyomi.lib.randomua.getPrefCustomUA
|
||||
import eu.kanade.tachiyomi.lib.randomua.getPrefUAType
|
||||
@ -27,6 +25,8 @@ import eu.kanade.tachiyomi.source.model.SManga
|
||||
import eu.kanade.tachiyomi.source.model.UpdateStrategy
|
||||
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
|
||||
import eu.kanade.tachiyomi.util.asJsoup
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import kotlinx.serialization.json.Json
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
@ -36,6 +36,7 @@ import org.jsoup.nodes.Element
|
||||
import rx.Observable
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
||||
open class NHentai(
|
||||
override val lang: String,
|
||||
@ -50,6 +51,8 @@ open class NHentai(
|
||||
|
||||
override val supportsLatest = true
|
||||
|
||||
private val json: Json by injectLazy()
|
||||
|
||||
private val preferences: SharedPreferences by lazy {
|
||||
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
|
||||
}
|
||||
@ -71,6 +74,7 @@ open class NHentai(
|
||||
}
|
||||
|
||||
private val shortenTitleRegex = Regex("""(\[[^]]*]|[({][^)}]*[)}])""")
|
||||
private val dataRegex = Regex("""JSON.parse\("([^*]*)"\)""")
|
||||
private fun String.shortenTitle() = this.replace(shortenTitleRegex, "").trim()
|
||||
|
||||
override fun setupPreferenceScreen(screen: PreferenceScreen) {
|
||||
@ -103,7 +107,7 @@ open class NHentai(
|
||||
title = element.select("a > div").text().replace("\"", "").let {
|
||||
if (displayFullTitle) it.trim() else it.shortenTitle()
|
||||
}
|
||||
thumbnail_url = element.select(".cover img").first()!!.let { img ->
|
||||
thumbnail_url = element.selectFirst(".cover img")!!.let { img ->
|
||||
if (img.hasAttr("data-src")) img.attr("abs:data-src") else img.attr("abs:src")
|
||||
}
|
||||
}
|
||||
@ -207,22 +211,25 @@ open class NHentai(
|
||||
override fun searchMangaNextPageSelector() = latestUpdatesNextPageSelector()
|
||||
|
||||
override fun mangaDetailsParse(document: Document): SManga {
|
||||
val fullTitle = document.select("#info > h1").text().replace("\"", "").trim()
|
||||
val script = document.selectFirst("script:containsData(JSON.parse)")!!.data()
|
||||
|
||||
val json = dataRegex.find(script)?.groupValues!![1]
|
||||
|
||||
val data = json.parseAs<Hentai>()
|
||||
return SManga.create().apply {
|
||||
title = if (displayFullTitle) fullTitle else fullTitle.shortenTitle()
|
||||
title = if (displayFullTitle) data.title.english ?: data.title.japanese ?: data.title.pretty!! else data.title.pretty ?: (data.title.english ?: data.title.japanese)!!.shortenTitle()
|
||||
thumbnail_url = document.select("#cover > a > img").attr("data-src")
|
||||
status = SManga.COMPLETED
|
||||
artist = getArtists(document)
|
||||
author = getGroups(document)
|
||||
artist = getArtists(data)
|
||||
author = getGroups(data)
|
||||
// Some people want these additional details in description
|
||||
description = "Full English and Japanese titles:\n"
|
||||
.plus("$fullTitle\n")
|
||||
.plus("${document.select("div#info h2").text()}\n\n")
|
||||
.plus("Pages: ${getNumPages(document)}\n")
|
||||
.plus("Favorited by: ${document.select("div#info i.fa-heart ~ span span").text().removeSurrounding("(", ")")}\n")
|
||||
.plus(getTagDescription(document))
|
||||
genre = getTags(document)
|
||||
.plus("${data.title.english}\n")
|
||||
.plus("${data.title.japanese}\n\n")
|
||||
.plus("Pages: ${data.images.pages.size}\n")
|
||||
.plus("Favorited by: ${data.num_favorites}\n")
|
||||
.plus(getTagDescription(data))
|
||||
genre = getTags(data)
|
||||
update_strategy = UpdateStrategy.ONLY_FETCH_ONCE
|
||||
}
|
||||
}
|
||||
@ -231,11 +238,16 @@ open class NHentai(
|
||||
|
||||
override fun chapterListParse(response: Response): List<SChapter> {
|
||||
val document = response.asJsoup()
|
||||
val script = document.selectFirst("script:containsData(JSON.parse)")!!.data()
|
||||
|
||||
val json = dataRegex.find(script)?.groupValues!![1]
|
||||
|
||||
val data = json.parseAs<Hentai>()
|
||||
return listOf(
|
||||
SChapter.create().apply {
|
||||
name = "Chapter"
|
||||
scanlator = getGroups(document)
|
||||
date_upload = getTime(document)
|
||||
scanlator = getGroups(data)
|
||||
date_upload = data.upload_date * 1000
|
||||
setUrlWithoutDomain(response.request.url.encodedPath)
|
||||
},
|
||||
)
|
||||
@ -246,11 +258,23 @@ open class NHentai(
|
||||
override fun chapterListSelector() = throw UnsupportedOperationException()
|
||||
|
||||
override fun pageListParse(document: Document): List<Page> {
|
||||
val script = document.select("script:containsData(media_server)").first()!!.data()
|
||||
val mediaServer = Regex("""media_server\s*:\s*(\d+)""").find(script)?.groupValues!![1]
|
||||
val script = document.selectFirst("script:containsData(media_server)")!!.data()
|
||||
val script2 = document.selectFirst("script:containsData(JSON.parse)")!!.data()
|
||||
|
||||
return document.select("div.thumbs a > img").mapIndexed { i, img ->
|
||||
Page(i, "", img.attr("abs:data-src").replace("t.nh", "i.nh").replace("t\\d+.nh".toRegex(), "i$mediaServer.nh").replace("t.", "."))
|
||||
val mediaServer = Regex("""media_server\s*:\s*(\d+)""").find(script)?.groupValues!![1]
|
||||
val json = dataRegex.find(script2)?.groupValues!![1]
|
||||
|
||||
val data = json.parseAs<Hentai>()
|
||||
return data.images.pages.mapIndexed { i, image ->
|
||||
Page(
|
||||
i,
|
||||
imageUrl = "${baseUrl.replace("https://", "https://i$mediaServer.")}/galleries/${data.media_id}/${i + 1}" +
|
||||
when (image.t) {
|
||||
"w" -> ".webp"
|
||||
"p" -> ".png"
|
||||
else -> ".jpg"
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -303,6 +327,13 @@ open class NHentai(
|
||||
),
|
||||
)
|
||||
|
||||
private inline fun <reified T> String.parseAs(): T {
|
||||
return json.decodeFromString(
|
||||
Regex("""\\u([0-9A-Fa-f]{4})""").replace(this) {
|
||||
it.groupValues[1].toInt(16).toChar().toString()
|
||||
},
|
||||
)
|
||||
}
|
||||
private open class UriPartFilter(displayName: String, val vals: Array<Pair<String, String>>) :
|
||||
Filter.Select<String>(displayName, vals.map { it.first }.toTypedArray()) {
|
||||
fun toUriPart() = vals[state].second
|
||||
|
Loading…
Reference in New Issue
Block a user