Merge branch 'KotatsuApp:master' into master

pull/223/head
devi 3 years ago committed by GitHub
commit 37e5c43e5c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -8,5 +8,7 @@ object UserAgents {
const val CHROME_DESKTOP = const val CHROME_DESKTOP =
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36"
const val FIREFOX_DESKTOP = "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/116.0"
const val KOTATSU = "Kotatsu/5.3 (Android 12;;; en)" const val KOTATSU = "Kotatsu/5.3 (Android 12;;; en)"
} }

@ -75,7 +75,7 @@ internal class BentomangaParser(context: MangaLoaderContext) : PagedMangaParser(
?.div(10f) ?.div(10f)
?: RATING_UNKNOWN, ?: RATING_UNKNOWN,
isNsfw = div.selectFirst(".badge-adult_content") != null, isNsfw = div.selectFirst(".badge-adult_content") != null,
coverUrl = div.selectFirstOrThrow("img").src(), coverUrl = div.selectFirstOrThrow("img").src().assertNotNull("src").orEmpty(),
tags = div.selectFirst(".component-manga-categories") tags = div.selectFirst(".component-manga-categories")
.assertNotNull("tags") .assertNotNull("tags")
?.select("a") ?.select("a")
@ -223,14 +223,4 @@ internal class BentomangaParser(context: MangaLoaderContext) : PagedMangaParser(
calendar.add(calendarUnit, -count) calendar.add(calendarUnit, -count)
return calendar.timeInMillis return calendar.timeInMillis
} }
private fun Element.src(): String {
return attrAsAbsoluteUrlOrNull("data-cfsrc")
?: attrAsAbsoluteUrlOrNull("src")
?: attrAsAbsoluteUrlOrNull("data-src")
?: run {
assert(false) { "Image src not found" }
""
}
}
} }

@ -24,7 +24,7 @@ internal class JapScanParser(context: MangaLoaderContext) : PagedMangaParser(con
override val configKeyDomain = ConfigKey.Domain("www.japscan.lol", "japscan.ws") override val configKeyDomain = ConfigKey.Domain("www.japscan.lol", "japscan.ws")
override val headers: Headers = Headers.Builder() override val headers: Headers = Headers.Builder()
.add("User-Agent", UserAgents.CHROME_MOBILE) .add("User-Agent", UserAgents.FIREFOX_DESKTOP)
.build() .build()
override suspend fun getListPage( override suspend fun getListPage(
@ -41,15 +41,15 @@ internal class JapScanParser(context: MangaLoaderContext) : PagedMangaParser(con
.addPathSegment("mangas") .addPathSegment("mangas")
.addPathSegment(page.toString()) .addPathSegment(page.toString())
.build() .build()
val root = webClient.httpGet(url).parseHtml() val doc = webClient.httpGet(url).parseHtml()
.requireElementById("main") val root = doc.requireElementById("main")
.selectFirstOrThrow(".p-2.row.d-flex") .selectFirstOrThrow("div.d-flex.flex-wrap")
return root.select("div.col-4") return root.select("div.p-2")
.map { div -> .map { div ->
val href = div.selectFirstOrThrow("a").attrAsRelativeUrl("href") val href = div.selectFirstOrThrow("a").attrAsRelativeUrl("href")
Manga( Manga(
id = generateUid(href), id = generateUid(href),
title = div.selectFirstOrThrow("p.p-1 a").text(), title = div.selectFirstOrThrow(".mainTitle").text(),
altTitle = null, altTitle = null,
url = href, url = href,
publicUrl = href.toAbsoluteUrl(domain), publicUrl = href.toAbsoluteUrl(domain),
@ -184,10 +184,8 @@ internal class JapScanParser(context: MangaLoaderContext) : PagedMangaParser(con
var key1 = calc1tab.joinToString("") var key1 = calc1tab.joinToString("")
var key2 = calc2tab.joinToString("") var key2 = calc2tab.joinToString("")
key1 = key1.replace("'", "") key1 = key1.filterNot { c -> c == '\'' || c == ' ' }
key2 = key2.replace("'", "") key2 = key2.filterNot { c -> c == '\'' || c == ' ' }
key1 = key1.replace(" ", "")
key2 = key2.replace(" ", "")
val keyTables = listOf( val keyTables = listOf(
key1.reversed(), key1.reversed(),

@ -543,13 +543,6 @@ internal abstract class MadaraParser(
} }
} }
protected fun Element.src(): String? {
var result = absUrl("data-src")
if (result.isEmpty()) result = absUrl("data-cfsrc")
if (result.isEmpty()) result = absUrl("src")
return result.ifEmpty { null }
}
private companion object { private companion object {
private fun createRequestTemplate() = private fun createRequestTemplate() =

@ -205,13 +205,4 @@ internal abstract class Manga18Parser(
) )
} }
} }
protected fun Element.src(): String? {
var result = absUrl("data-src")
if (result.isEmpty()) result = absUrl("data-cfsrc")
if (result.isEmpty()) result = absUrl("src")
return result.ifEmpty { null }
}
} }

@ -1,23 +1,11 @@
package org.koitharu.kotatsu.parsers.site.madara.en package org.koitharu.kotatsu.parsers.site.manga18.en
import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.ContentType import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.model.MangaTag
import org.koitharu.kotatsu.parsers.model.RATING_UNKNOWN
import org.koitharu.kotatsu.parsers.model.SortOrder
import org.koitharu.kotatsu.parsers.site.manga18.Manga18Parser import org.koitharu.kotatsu.parsers.site.manga18.Manga18Parser
import org.koitharu.kotatsu.parsers.util.attrAsRelativeUrl import org.koitharu.kotatsu.parsers.util.*
import org.koitharu.kotatsu.parsers.util.domain
import org.koitharu.kotatsu.parsers.util.generateUid
import org.koitharu.kotatsu.parsers.util.host
import org.koitharu.kotatsu.parsers.util.parseHtml
import org.koitharu.kotatsu.parsers.util.selectFirstOrThrow
import org.koitharu.kotatsu.parsers.util.toAbsoluteUrl
import org.koitharu.kotatsu.parsers.util.urlEncoded
@MangaSourceParser("HENTAI3ZCC", "Hentai3z Cc", "en", ContentType.HENTAI) @MangaSourceParser("HENTAI3ZCC", "Hentai3z Cc", "en", ContentType.HENTAI)

@ -27,11 +27,4 @@ internal class LelManga(context: MangaLoaderContext) :
) )
} }
} }
private fun Element.src(): String? {
var result = absUrl("data-src")
if (result.isEmpty()) result = absUrl("data-cfsrc")
if (result.isEmpty()) result = absUrl("src")
return result.ifEmpty { null }
}
} }

@ -3,7 +3,6 @@ package org.koitharu.kotatsu.parsers.site.mmrcms
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.coroutineScope
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.PagedMangaParser import org.koitharu.kotatsu.parsers.PagedMangaParser
import org.koitharu.kotatsu.parsers.config.ConfigKey import org.koitharu.kotatsu.parsers.config.ConfigKey
@ -248,13 +247,4 @@ internal abstract class MmrcmsParser(
) )
} }
} }
protected fun Element.src(): String? {
var result = absUrl("data-src")
if (result.isEmpty()) result = absUrl("data-cfsrc")
if (result.isEmpty()) result = absUrl("src")
return result.ifEmpty { null }
}
} }

@ -23,7 +23,7 @@ val Element.host: String?
* Return an attribute value or null if it is missing or empty * Return an attribute value or null if it is missing or empty
* @see [Element.attr] which returns empty string instead of null * @see [Element.attr] which returns empty string instead of null
*/ */
fun Element.attrOrNull(attributeKey: String) = attr(attributeKey).takeUnless { it.isEmpty() } fun Element.attrOrNull(attributeKey: String) = attr(attributeKey).takeUnless { it.isBlank() }?.trim()
/** /**
@ -43,7 +43,7 @@ fun Element.attrOrThrow(attributeKey: String): String = if (hasAttr(attributeKey
* @see attrAsAbsoluteUrl * @see attrAsAbsoluteUrl
*/ */
fun Element.attrAsRelativeUrlOrNull(attributeKey: String): String? { fun Element.attrAsRelativeUrlOrNull(attributeKey: String): String? {
val attr = attr(attributeKey).trim() val attr = attrOrNull(attributeKey) ?: return null
if (attr.isEmpty() || attr.startsWith("data:")) { if (attr.isEmpty() || attr.startsWith("data:")) {
return null return null
} }
@ -74,7 +74,7 @@ fun Element.attrAsRelativeUrl(attributeKey: String): String {
* @see attrAsRelativeUrlOrNull * @see attrAsRelativeUrlOrNull
*/ */
fun Element.attrAsAbsoluteUrlOrNull(attributeKey: String): String? { fun Element.attrAsAbsoluteUrlOrNull(attributeKey: String): String? {
val attr = attr(attributeKey).trim() val attr = attrOrNull(attributeKey) ?: return null
if (attr.isEmpty() || attr.startsWith("data:")) { if (attr.isEmpty() || attr.startsWith("data:")) {
return null return null
} }
@ -140,3 +140,27 @@ fun Element.selectFirstParent(query: String): Element? {
selector.matches(root, it) selector.matches(root, it)
} }
} }
/**
* Return a first non-empty attribute value of [names] or null if it is missing or empty
*/
fun Element.attrOrNull(vararg names: String): String? {
for (name in names) {
val value = attr(name)
if (value.isNotEmpty()) {
return value.trim()
}
}
return null
}
@JvmOverloads
fun Element.src(names: Array<String> = arrayOf("data-src", "data-cfsrc", "src")): String? {
for (name in names) {
val value = attrAsAbsoluteUrlOrNull(name)
if (value != null) {
return value
}
}
return null
}

@ -13,3 +13,17 @@ inline fun <T, R> T.runCatchingCancellable(block: T.() -> R): Result<R> {
Result.failure(e) Result.failure(e)
} }
} }
inline fun <R, T : R> Result<T>.recoverCatchingCancellable(transform: (exception: Throwable) -> R): Result<R> {
return when (val exception = exceptionOrNull()) {
null -> this
else -> runCatchingCancellable { transform(exception) }
}
}
inline fun <R : Any, T : R> Result<T>.recoverNotNull(transform: (exception: Throwable) -> R?): Result<R> {
return when (val exception = exceptionOrNull()) {
null -> this
else -> transform(exception)?.let(Result.Companion::success) ?: this
}
}

Loading…
Cancel
Save