Refactor extensions
This commit is contained in:
@@ -50,7 +50,7 @@ dependencies {
|
|||||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.1'
|
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.1'
|
||||||
implementation 'com.squareup.okhttp3:okhttp:4.9.3'
|
implementation 'com.squareup.okhttp3:okhttp:4.9.3'
|
||||||
implementation 'com.squareup.okio:okio:3.1.0'
|
implementation 'com.squareup.okio:okio:3.1.0'
|
||||||
implementation 'org.jsoup:jsoup:1.14.3'
|
implementation 'org.jsoup:jsoup:1.15.1'
|
||||||
implementation 'org.json:json:20220320'
|
implementation 'org.json:json:20220320'
|
||||||
implementation 'androidx.collection:collection-ktx:1.2.0'
|
implementation 'androidx.collection:collection-ktx:1.2.0'
|
||||||
|
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ internal class AnibelParser(override val context: MangaLoaderContext) : MangaPar
|
|||||||
genres
|
genres
|
||||||
slug
|
slug
|
||||||
mediaType
|
mediaType
|
||||||
status
|
status
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
""".trimIndent(),
|
""".trimIndent(),
|
||||||
|
|||||||
@@ -189,7 +189,7 @@ internal class BatoToParser(override val context: MangaLoaderContext) : MangaPar
|
|||||||
val root = body.getElementById("series-list") ?: parseFailed("Cannot find root")
|
val root = body.getElementById("series-list") ?: parseFailed("Cannot find root")
|
||||||
return root.children().map { div ->
|
return root.children().map { div ->
|
||||||
val a = div.selectFirst("a") ?: parseFailed()
|
val a = div.selectFirst("a") ?: parseFailed()
|
||||||
val href = a.relUrl("href")
|
val href = a.attrAsRelativeUrl("href")
|
||||||
val title = div.selectFirst(".item-title")?.text() ?: parseFailed("Title not found")
|
val title = div.selectFirst(".item-title")?.text() ?: parseFailed("Title not found")
|
||||||
Manga(
|
Manga(
|
||||||
id = generateUid(href),
|
id = generateUid(href),
|
||||||
@@ -222,7 +222,7 @@ internal class BatoToParser(override val context: MangaLoaderContext) : MangaPar
|
|||||||
private fun Element.parseChapter(index: Int): MangaChapter? {
|
private fun Element.parseChapter(index: Int): MangaChapter? {
|
||||||
val a = selectFirst("a.chapt") ?: return null
|
val a = selectFirst("a.chapt") ?: return null
|
||||||
val extra = selectFirst(".extra")
|
val extra = selectFirst(".extra")
|
||||||
val href = a.relUrl("href")
|
val href = a.attrAsRelativeUrl("href")
|
||||||
return MangaChapter(
|
return MangaChapter(
|
||||||
id = generateUid(href),
|
id = generateUid(href),
|
||||||
name = a.text(),
|
name = a.text(),
|
||||||
|
|||||||
@@ -42,11 +42,11 @@ internal abstract class ChanParser(source: MangaSource) : MangaParser(source) {
|
|||||||
return root.select("div.content_row").mapNotNull { row ->
|
return root.select("div.content_row").mapNotNull { row ->
|
||||||
val a = row.selectFirst("div.manga_row1")?.selectFirst("h2")?.selectFirst("a")
|
val a = row.selectFirst("div.manga_row1")?.selectFirst("h2")?.selectFirst("a")
|
||||||
?: return@mapNotNull null
|
?: return@mapNotNull null
|
||||||
val href = a.relUrl("href")
|
val href = a.attrAsRelativeUrl("href")
|
||||||
Manga(
|
Manga(
|
||||||
id = generateUid(href),
|
id = generateUid(href),
|
||||||
url = href,
|
url = href,
|
||||||
publicUrl = href.inContextOf(a),
|
publicUrl = href.toAbsoluteUrl(a.host ?: domain),
|
||||||
altTitle = a.attr("title"),
|
altTitle = a.attr("title"),
|
||||||
title = a.text().substringAfterLast('(').substringBeforeLast(')'),
|
title = a.text().substringAfterLast('(').substringBeforeLast(')'),
|
||||||
author = row.getElementsByAttributeValueStarting(
|
author = row.getElementsByAttributeValueStarting(
|
||||||
@@ -81,7 +81,8 @@ internal abstract class ChanParser(source: MangaSource) : MangaParser(source) {
|
|||||||
description = root.getElementById("description")?.html()?.substringBeforeLast("<div"),
|
description = root.getElementById("description")?.html()?.substringBeforeLast("<div"),
|
||||||
largeCoverUrl = root.getElementById("cover")?.absUrl("src"),
|
largeCoverUrl = root.getElementById("cover")?.absUrl("src"),
|
||||||
chapters = root.select("table.table_cha tr:gt(1)").reversed().mapIndexedNotNull { i, tr ->
|
chapters = root.select("table.table_cha tr:gt(1)").reversed().mapIndexedNotNull { i, tr ->
|
||||||
val href = tr?.selectFirst("a")?.relUrl("href") ?: return@mapIndexedNotNull null
|
val href = tr?.selectFirst("a")?.attrAsRelativeUrlOrNull("href")
|
||||||
|
?: return@mapIndexedNotNull null
|
||||||
MangaChapter(
|
MangaChapter(
|
||||||
id = generateUid(href),
|
id = generateUid(href),
|
||||||
name = tr.selectFirst("a")?.text().orEmpty(),
|
name = tr.selectFirst("a")?.text().orEmpty(),
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ internal class ExHentaiParser(
|
|||||||
val (td1, td2) = tr.children()
|
val (td1, td2) = tr.children()
|
||||||
val glink = td2.selectFirst("div.glink") ?: parseFailed("glink not found")
|
val glink = td2.selectFirst("div.glink") ?: parseFailed("glink not found")
|
||||||
val a = glink.parents().select("a").first() ?: parseFailed("link not found")
|
val a = glink.parents().select("a").first() ?: parseFailed("link not found")
|
||||||
val href = a.relUrl("href")
|
val href = a.attrAsRelativeUrl("href")
|
||||||
val tagsDiv = glink.nextElementSibling() ?: parseFailed("tags div not found")
|
val tagsDiv = glink.nextElementSibling() ?: parseFailed("tags div not found")
|
||||||
val mainTag = td2.selectFirst("div.cn")?.let { div ->
|
val mainTag = td2.selectFirst("div.cn")?.let { div ->
|
||||||
MangaTag(
|
MangaTag(
|
||||||
@@ -148,7 +148,7 @@ internal class ExHentaiParser(
|
|||||||
?.substringAfterLast(' ')
|
?.substringAfterLast(' ')
|
||||||
?.toFloatOrNull()
|
?.toFloatOrNull()
|
||||||
?.div(5f) ?: manga.rating,
|
?.div(5f) ?: manga.rating,
|
||||||
largeCoverUrl = cover?.css("background")?.cssUrl(),
|
largeCoverUrl = cover?.styleValueOrNull("background")?.cssUrl(),
|
||||||
description = taglist?.select("tr")?.joinToString("<br>") { tr ->
|
description = taglist?.select("tr")?.joinToString("<br>") { tr ->
|
||||||
val (tc, td) = tr.children()
|
val (tc, td) = tr.children()
|
||||||
val subtags = td.select("a").joinToString { it.html() }
|
val subtags = td.select("a").joinToString { it.html() }
|
||||||
@@ -181,7 +181,7 @@ internal class ExHentaiParser(
|
|||||||
val doc = context.httpGet(chapter.url.withDomain()).parseHtml()
|
val doc = context.httpGet(chapter.url.withDomain()).parseHtml()
|
||||||
val root = doc.body().getElementById("gdt") ?: parseFailed("Root not found")
|
val root = doc.body().getElementById("gdt") ?: parseFailed("Root not found")
|
||||||
return root.select("a").map { a ->
|
return root.select("a").map { a ->
|
||||||
val url = a.relUrl("href")
|
val url = a.attrAsRelativeUrl("href")
|
||||||
MangaPage(
|
MangaPage(
|
||||||
id = generateUid(url),
|
id = generateUid(url),
|
||||||
url = url,
|
url = url,
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ internal abstract class GroupleParser(source: MangaSource, userAgent: String) :
|
|||||||
if (descDiv.selectFirst("i.fa-user") != null) {
|
if (descDiv.selectFirst("i.fa-user") != null) {
|
||||||
return@mapNotNull null // skip author
|
return@mapNotNull null // skip author
|
||||||
}
|
}
|
||||||
val href = imgDiv.selectFirst("a")?.attr("href")?.inContextOf(node)
|
val href = imgDiv.selectFirst("a")?.attrAsAbsoluteUrlOrNull("href")
|
||||||
if (href == null || href.toHttpUrl().host != baseHost) {
|
if (href == null || href.toHttpUrl().host != baseHost) {
|
||||||
return@mapNotNull null // skip external links
|
return@mapNotNull null // skip external links
|
||||||
}
|
}
|
||||||
@@ -148,7 +148,7 @@ internal abstract class GroupleParser(source: MangaSource, userAgent: String) :
|
|||||||
chapters = root.selectFirst("div.chapters-link")?.selectFirst("table")
|
chapters = root.selectFirst("div.chapters-link")?.selectFirst("table")
|
||||||
?.select("tr:has(td > a)")?.asReversed()?.mapIndexedNotNull { i, tr ->
|
?.select("tr:has(td > a)")?.asReversed()?.mapIndexedNotNull { i, tr ->
|
||||||
val a = tr.selectFirst("a") ?: return@mapIndexedNotNull null
|
val a = tr.selectFirst("a") ?: return@mapIndexedNotNull null
|
||||||
val href = a.relUrl("href")
|
val href = a.attrAsRelativeUrl("href")
|
||||||
var translators = ""
|
var translators = ""
|
||||||
val translatorElement = a.attr("title")
|
val translatorElement = a.attr("title")
|
||||||
if (!translatorElement.isNullOrBlank()) {
|
if (!translatorElement.isNullOrBlank()) {
|
||||||
|
|||||||
@@ -55,12 +55,13 @@ internal abstract class MadaraParser(
|
|||||||
payload,
|
payload,
|
||||||
).parseHtml()
|
).parseHtml()
|
||||||
return doc.select("div.row.c-tabs-item__content").map { div ->
|
return doc.select("div.row.c-tabs-item__content").map { div ->
|
||||||
val href = div.selectFirst("a")?.relUrl("href") ?: parseFailed("Link not found")
|
val href = div.selectFirst("a")?.attrAsRelativeUrlOrNull("href")
|
||||||
|
?: parseFailed("Link not found")
|
||||||
val summary = div.selectFirst(".tab-summary")
|
val summary = div.selectFirst(".tab-summary")
|
||||||
Manga(
|
Manga(
|
||||||
id = generateUid(href),
|
id = generateUid(href),
|
||||||
url = href,
|
url = href,
|
||||||
publicUrl = href.inContextOf(div),
|
publicUrl = href.toAbsoluteUrl(div.host ?: getDomain()),
|
||||||
coverUrl = div.selectFirst("img")?.src().orEmpty(),
|
coverUrl = div.selectFirst("img")?.src().orEmpty(),
|
||||||
title = (summary?.selectFirst("h3") ?: summary?.selectFirst("h4"))?.text().orEmpty(),
|
title = (summary?.selectFirst("h3") ?: summary?.selectFirst("h4"))?.text().orEmpty(),
|
||||||
altTitle = null,
|
altTitle = null,
|
||||||
@@ -140,12 +141,10 @@ internal abstract class MadaraParser(
|
|||||||
?.joinToString { it.html() },
|
?.joinToString { it.html() },
|
||||||
chapters = root2.select("li").asReversed().mapIndexed { i, li ->
|
chapters = root2.select("li").asReversed().mapIndexed { i, li ->
|
||||||
val a = li.selectFirst("a")
|
val a = li.selectFirst("a")
|
||||||
val href = a?.relUrl("href").orEmpty().ifEmpty {
|
val href = a?.attrAsRelativeUrlOrNull("href") ?: parseFailed("Link is missing")
|
||||||
parseFailed("Link is missing")
|
|
||||||
}
|
|
||||||
MangaChapter(
|
MangaChapter(
|
||||||
id = generateUid(href),
|
id = generateUid(href),
|
||||||
name = a!!.ownText(),
|
name = a.ownText(),
|
||||||
number = i + 1,
|
number = i + 1,
|
||||||
url = href,
|
url = href,
|
||||||
uploadDate = parseChapterDate(
|
uploadDate = parseChapterDate(
|
||||||
@@ -361,7 +360,8 @@ internal abstract class MadaraParser(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@MangaSourceParser("HENTAI_4FREE", "Hentai4Free", "en")
|
@MangaSourceParser("HENTAI_4FREE", "Hentai4Free", "en")
|
||||||
class Hentai4Free(context: MangaLoaderContext) : MadaraParser(context, MangaSource.HENTAI_4FREE, "hentai4free.net") {
|
class Hentai4Free(context: MangaLoaderContext) :
|
||||||
|
MadaraParser(context, MangaSource.HENTAI_4FREE, "hentai4free.net") {
|
||||||
|
|
||||||
override val tagPrefix = "hentai-tag/"
|
override val tagPrefix = "hentai-tag/"
|
||||||
|
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ internal open class MangaLibParser(
|
|||||||
?: return emptyList()
|
?: return emptyList()
|
||||||
return items.mapNotNull { card ->
|
return items.mapNotNull { card ->
|
||||||
val a = card.selectFirst("a.media-card") ?: return@mapNotNull null
|
val a = card.selectFirst("a.media-card") ?: return@mapNotNull null
|
||||||
val href = a.relUrl("href")
|
val href = a.attrAsRelativeUrl("href")
|
||||||
Manga(
|
Manga(
|
||||||
id = generateUid(href),
|
id = generateUid(href),
|
||||||
title = card.selectFirst("h3")?.text().orEmpty(),
|
title = card.selectFirst("h3")?.text().orEmpty(),
|
||||||
@@ -74,7 +74,7 @@ internal open class MangaLibParser(
|
|||||||
author = null,
|
author = null,
|
||||||
rating = RATING_UNKNOWN,
|
rating = RATING_UNKNOWN,
|
||||||
url = href,
|
url = href,
|
||||||
publicUrl = href.inContextOf(a),
|
publicUrl = href.toAbsoluteUrl(a.host ?: getDomain()),
|
||||||
tags = emptySet(),
|
tags = emptySet(),
|
||||||
state = null,
|
state = null,
|
||||||
isNsfw = false,
|
isNsfw = false,
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ internal class MangaOwlParser(override val context: MangaLoaderContext) : MangaP
|
|||||||
val slides = doc.body().select("ul.slides") ?: parseFailed("An error occurred while parsing")
|
val slides = doc.body().select("ul.slides") ?: parseFailed("An error occurred while parsing")
|
||||||
val items = slides.select("div.col-md-2")
|
val items = slides.select("div.col-md-2")
|
||||||
return items.mapNotNull { item ->
|
return items.mapNotNull { item ->
|
||||||
val href = item.selectFirst("h6 a")?.relUrl("href") ?: return@mapNotNull null
|
val href = item.selectFirst("h6 a")?.attrAsRelativeUrlOrNull("href") ?: return@mapNotNull null
|
||||||
Manga(
|
Manga(
|
||||||
id = generateUid(href),
|
id = generateUid(href),
|
||||||
title = item.selectFirst("h6 a")?.text() ?: return@mapNotNull null,
|
title = item.selectFirst("h6 a")?.text() ?: return@mapNotNull null,
|
||||||
@@ -134,7 +134,7 @@ internal class MangaOwlParser(override val context: MangaLoaderContext) : MangaP
|
|||||||
val doc = context.httpGet(fullUrl).parseHtml()
|
val doc = context.httpGet(fullUrl).parseHtml()
|
||||||
val root = doc.body().select("div.item img.owl-lazy") ?: throw ParseException("Root not found")
|
val root = doc.body().select("div.item img.owl-lazy") ?: throw ParseException("Root not found")
|
||||||
return root.map { div ->
|
return root.map { div ->
|
||||||
val url = div?.relUrl("data-src") ?: parseFailed("Page image not found")
|
val url = div?.attrAsRelativeUrlOrNull("data-src") ?: parseFailed("Page image not found")
|
||||||
MangaPage(
|
MangaPage(
|
||||||
id = generateUid(url),
|
id = generateUid(url),
|
||||||
url = url,
|
url = url,
|
||||||
|
|||||||
@@ -58,10 +58,10 @@ internal class MangaTownParser(override val context: MangaLoaderContext) : Manga
|
|||||||
?: throw ParseException("Root not found")
|
?: throw ParseException("Root not found")
|
||||||
return root.select("li").mapNotNull { li ->
|
return root.select("li").mapNotNull { li ->
|
||||||
val a = li.selectFirst("a.manga_cover")
|
val a = li.selectFirst("a.manga_cover")
|
||||||
val href = a?.relUrl("href")
|
val href = a?.attrAsRelativeUrlOrNull("href")
|
||||||
?: return@mapNotNull null
|
?: return@mapNotNull null
|
||||||
val views = li.select("p.view")
|
val views = li.select("p.view")
|
||||||
val status = views.findOwnText { x -> x.startsWith("Status:") }
|
val status = views.firstNotNullOfOrNull { it.ownText().takeIf { x -> x.startsWith("Status:") } }
|
||||||
?.substringAfter(':')?.trim()?.lowercase(Locale.ROOT)
|
?.substringAfter(':')?.trim()?.lowercase(Locale.ROOT)
|
||||||
Manga(
|
Manga(
|
||||||
id = generateUid(href),
|
id = generateUid(href),
|
||||||
@@ -71,7 +71,8 @@ internal class MangaTownParser(override val context: MangaLoaderContext) : Manga
|
|||||||
altTitle = null,
|
altTitle = null,
|
||||||
rating = li.selectFirst("p.score")?.selectFirst("b")
|
rating = li.selectFirst("p.score")?.selectFirst("b")
|
||||||
?.ownText()?.toFloatOrNull()?.div(5f) ?: RATING_UNKNOWN,
|
?.ownText()?.toFloatOrNull()?.div(5f) ?: RATING_UNKNOWN,
|
||||||
author = views.findText { x -> x.startsWith("Author:") }?.substringAfter(':')
|
author = views.firstNotNullOfOrNull { it.text().takeIf { x -> x.startsWith("Author:") } }
|
||||||
|
?.substringAfter(':')
|
||||||
?.trim(),
|
?.trim(),
|
||||||
state = when (status) {
|
state = when (status) {
|
||||||
"ongoing" -> MangaState.ONGOING
|
"ongoing" -> MangaState.ONGOING
|
||||||
@@ -87,7 +88,7 @@ internal class MangaTownParser(override val context: MangaLoaderContext) : Manga
|
|||||||
}.orEmpty(),
|
}.orEmpty(),
|
||||||
url = href,
|
url = href,
|
||||||
isNsfw = false,
|
isNsfw = false,
|
||||||
publicUrl = href.inContextOf(a),
|
publicUrl = href.toAbsoluteUrl(a.host ?: getDomain()),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -112,9 +113,10 @@ internal class MangaTownParser(override val context: MangaLoaderContext) : Manga
|
|||||||
}.orEmpty(),
|
}.orEmpty(),
|
||||||
description = info?.getElementById("show")?.ownText(),
|
description = info?.getElementById("show")?.ownText(),
|
||||||
chapters = chaptersList?.mapIndexedNotNull { i, li ->
|
chapters = chaptersList?.mapIndexedNotNull { i, li ->
|
||||||
val href = li.selectFirst("a")?.relUrl("href")
|
val href = li.selectFirst("a")?.attrAsRelativeUrlOrNull("href")
|
||||||
?: return@mapIndexedNotNull null
|
?: return@mapIndexedNotNull null
|
||||||
val name = li.select("span").filter { it.className().isEmpty() }
|
val name = li.select("span")
|
||||||
|
.filter { x -> x.className().isEmpty() }
|
||||||
.joinToString(" - ") { it.text() }.trim()
|
.joinToString(" - ") { it.text() }.trim()
|
||||||
MangaChapter(
|
MangaChapter(
|
||||||
id = generateUid(href),
|
id = generateUid(href),
|
||||||
@@ -139,8 +141,8 @@ internal class MangaTownParser(override val context: MangaLoaderContext) : Manga
|
|||||||
val root = doc.body().selectFirst("div.page_select")
|
val root = doc.body().selectFirst("div.page_select")
|
||||||
?: throw ParseException("Cannot find root")
|
?: throw ParseException("Cannot find root")
|
||||||
return root.selectFirst("select")?.select("option")?.mapNotNull {
|
return root.selectFirst("select")?.select("option")?.mapNotNull {
|
||||||
val href = it.relUrl("value")
|
val href = it.attrAsRelativeUrlOrNull("value")
|
||||||
if (href.endsWith("featured.html")) {
|
if (href == null || href.endsWith("featured.html")) {
|
||||||
return@mapNotNull null
|
return@mapNotNull null
|
||||||
}
|
}
|
||||||
MangaPage(
|
MangaPage(
|
||||||
@@ -193,7 +195,7 @@ internal class MangaTownParser(override val context: MangaLoaderContext) : Manga
|
|||||||
val dateFormat = SimpleDateFormat("MMM dd,yyyy", Locale.US)
|
val dateFormat = SimpleDateFormat("MMM dd,yyyy", Locale.US)
|
||||||
return list.select("li").asReversed().mapIndexedNotNull { i, li ->
|
return list.select("li").asReversed().mapIndexedNotNull { i, li ->
|
||||||
val a = li.selectFirst("a") ?: return@mapIndexedNotNull null
|
val a = li.selectFirst("a") ?: return@mapIndexedNotNull null
|
||||||
val href = a.relUrl("href")
|
val href = a.attrAsRelativeUrl("href")
|
||||||
val name = a.selectFirst("span.vol")?.text().orEmpty().ifEmpty {
|
val name = a.selectFirst("span.vol")?.text().orEmpty().ifEmpty {
|
||||||
a.ownText()
|
a.ownText()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -117,7 +117,8 @@ internal abstract class NineMangaParser(
|
|||||||
chapters = root.selectFirst("div.chapterbox")?.select("ul.sub_vol_ul > li")
|
chapters = root.selectFirst("div.chapterbox")?.select("ul.sub_vol_ul > li")
|
||||||
?.asReversed()?.mapIndexed { i, li ->
|
?.asReversed()?.mapIndexed { i, li ->
|
||||||
val a = li.selectFirst("a.chapter_list_a")
|
val a = li.selectFirst("a.chapter_list_a")
|
||||||
val href = a?.relUrl("href")?.replace("%20", " ") ?: parseFailed("Link not found")
|
val href = a?.attrAsRelativeUrlOrNull("href")
|
||||||
|
?.replace("%20", " ") ?: parseFailed("Link not found")
|
||||||
MangaChapter(
|
MangaChapter(
|
||||||
id = generateUid(href),
|
id = generateUid(href),
|
||||||
name = a.text(),
|
name = a.text(),
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ internal class NudeMoonParser(
|
|||||||
return root.select("table.news_pic2").mapNotNull { row ->
|
return root.select("table.news_pic2").mapNotNull { row ->
|
||||||
val a = row.selectFirst("td.bg_style1")?.selectFirst("a")
|
val a = row.selectFirst("td.bg_style1")?.selectFirst("a")
|
||||||
?: return@mapNotNull null
|
?: return@mapNotNull null
|
||||||
val href = a.relUrl("href")
|
val href = a.attrAsRelativeUrl("href")
|
||||||
val title = a.selectFirst("h2")?.text().orEmpty()
|
val title = a.selectFirst("h2")?.text().orEmpty()
|
||||||
val info = row.selectFirst("td[width=100%]") ?: return@mapNotNull null
|
val info = row.selectFirst("td[width=100%]") ?: return@mapNotNull null
|
||||||
Manga(
|
Manga(
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ import org.koitharu.kotatsu.parsers.exception.ParseException
|
|||||||
import org.koitharu.kotatsu.parsers.model.Manga
|
import org.koitharu.kotatsu.parsers.model.Manga
|
||||||
import org.koitharu.kotatsu.parsers.model.MangaChapter
|
import org.koitharu.kotatsu.parsers.model.MangaChapter
|
||||||
import org.koitharu.kotatsu.parsers.model.MangaSource
|
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||||
|
import org.koitharu.kotatsu.parsers.util.attrAsRelativeUrl
|
||||||
import org.koitharu.kotatsu.parsers.util.parseHtml
|
import org.koitharu.kotatsu.parsers.util.parseHtml
|
||||||
import org.koitharu.kotatsu.parsers.util.relUrl
|
|
||||||
|
|
||||||
@MangaSourceParser("YAOICHAN", "Яой-тян", "ru")
|
@MangaSourceParser("YAOICHAN", "Яой-тян", "ru")
|
||||||
internal class YaoiChanParser(override val context: MangaLoaderContext) : ChanParser(MangaSource.YAOICHAN) {
|
internal class YaoiChanParser(override val context: MangaLoaderContext) : ChanParser(MangaSource.YAOICHAN) {
|
||||||
@@ -25,7 +25,7 @@ internal class YaoiChanParser(override val context: MangaLoaderContext) : ChanPa
|
|||||||
chapters = root.select("table.table_cha").flatMap { table ->
|
chapters = root.select("table.table_cha").flatMap { table ->
|
||||||
table.select("div.manga")
|
table.select("div.manga")
|
||||||
}.mapNotNull { it.selectFirst("a") }.reversed().mapIndexed { i, a ->
|
}.mapNotNull { it.selectFirst("a") }.reversed().mapIndexed { i, a ->
|
||||||
val href = a.relUrl("href")
|
val href = a.attrAsRelativeUrl("href")
|
||||||
MangaChapter(
|
MangaChapter(
|
||||||
id = generateUid(href),
|
id = generateUid(href),
|
||||||
name = a.text().trim(),
|
name = a.text().trim(),
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
@file:JvmName("CollectionUtils")
|
||||||
|
|
||||||
package org.koitharu.kotatsu.parsers.util
|
package org.koitharu.kotatsu.parsers.util
|
||||||
|
|
||||||
import androidx.collection.ArrayMap
|
import androidx.collection.ArrayMap
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
@file:JvmName("CookieJarUtils")
|
||||||
|
|
||||||
package org.koitharu.kotatsu.parsers.util
|
package org.koitharu.kotatsu.parsers.util
|
||||||
|
|
||||||
import okhttp3.Cookie
|
import okhttp3.Cookie
|
||||||
56
src/main/kotlin/org/koitharu/kotatsu/parsers/util/Jsoup.kt
Normal file
56
src/main/kotlin/org/koitharu/kotatsu/parsers/util/Jsoup.kt
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
@file:JvmName("JsoupUtils")
|
||||||
|
|
||||||
|
package org.koitharu.kotatsu.parsers.util
|
||||||
|
|
||||||
|
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||||
|
import org.jsoup.nodes.Element
|
||||||
|
|
||||||
|
val Element.host: String?
|
||||||
|
get() {
|
||||||
|
val uri = baseUri()
|
||||||
|
return if (uri.isEmpty()) {
|
||||||
|
null
|
||||||
|
} else {
|
||||||
|
uri.toHttpUrlOrNull()?.host
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Element.attrOrNull(attributeKey: String) = attr(attributeKey).takeUnless { it.isEmpty() }
|
||||||
|
|
||||||
|
fun Element.attrAsRelativeUrlOrNull(attributeKey: String): String? {
|
||||||
|
val attr = attr(attributeKey).trim()
|
||||||
|
if (attr.isEmpty()) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
if (attr.startsWith("/")) {
|
||||||
|
return attr
|
||||||
|
}
|
||||||
|
val host = baseUri().toHttpUrlOrNull()?.host ?: return null
|
||||||
|
return attr.substringAfter(host)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Element.attrAsRelativeUrl(attributeKey: String): String {
|
||||||
|
return requireNotNull(attrAsRelativeUrlOrNull(attributeKey)) {
|
||||||
|
"Cannot get relative url for $attributeKey: \"${attr(attributeKey)}\""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Element.attrAsAbsoluteUrlOrNull(attributeKey: String): String? {
|
||||||
|
val attr = attr(attributeKey).trim()
|
||||||
|
if (attr.isEmpty()) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return (baseUri().toHttpUrlOrNull()?.newBuilder(attr) ?: return null).toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Element.attrAsAbsoluteUrl(attributeKey: String): String {
|
||||||
|
return requireNotNull(attrAsAbsoluteUrlOrNull(attributeKey)) {
|
||||||
|
"Cannot get absolute url for $attributeKey: \"${attr(attributeKey)}\""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Element.styleValueOrNull(property: String): String? {
|
||||||
|
val regex = Regex("${Regex.escape(property)}\\s*:\\s*[^;]+")
|
||||||
|
val css = attr("style").find(regex) ?: return null
|
||||||
|
return css.substringAfter(':').removeSuffix(';').trim()
|
||||||
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
@file:JvmName("NumberUtils")
|
||||||
|
|
||||||
package org.koitharu.kotatsu.parsers.util
|
package org.koitharu.kotatsu.parsers.util
|
||||||
|
|
||||||
import java.text.DecimalFormat
|
import java.text.DecimalFormat
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
@file:JvmName("OkHttpUtils")
|
||||||
|
|
||||||
package org.koitharu.kotatsu.parsers.util
|
package org.koitharu.kotatsu.parsers.util
|
||||||
|
|
||||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
@file:JvmName("ParseUtils")
|
||||||
|
|
||||||
package org.koitharu.kotatsu.parsers.util
|
package org.koitharu.kotatsu.parsers.util
|
||||||
|
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
@@ -45,6 +47,11 @@ fun Response.parseJsonArray(): JSONArray {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated(
|
||||||
|
message = "",
|
||||||
|
level = DeprecationLevel.ERROR,
|
||||||
|
replaceWith = ReplaceWith("firstNotNullOfOrNull { it.ownText().takeIf(predicate) }"),
|
||||||
|
)
|
||||||
inline fun Elements.findOwnText(predicate: (String) -> Boolean): String? {
|
inline fun Elements.findOwnText(predicate: (String) -> Boolean): String? {
|
||||||
for (x in this) {
|
for (x in this) {
|
||||||
val ownText = x.ownText()
|
val ownText = x.ownText()
|
||||||
@@ -55,6 +62,11 @@ inline fun Elements.findOwnText(predicate: (String) -> Boolean): String? {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated(
|
||||||
|
message = "",
|
||||||
|
level = DeprecationLevel.ERROR,
|
||||||
|
replaceWith = ReplaceWith("firstNotNullOfOrNull { it.text().takeIf(predicate) }"),
|
||||||
|
)
|
||||||
inline fun Elements.findText(predicate: (String) -> Boolean): String? {
|
inline fun Elements.findText(predicate: (String) -> Boolean): String? {
|
||||||
for (x in this) {
|
for (x in this) {
|
||||||
val text = x.text()
|
val text = x.text()
|
||||||
@@ -65,6 +77,11 @@ inline fun Elements.findText(predicate: (String) -> Boolean): String? {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated(
|
||||||
|
message = "Use toAbsoluteUrl() instead",
|
||||||
|
level = DeprecationLevel.ERROR,
|
||||||
|
replaceWith = ReplaceWith("toAbsoluteUrl(node.host)"),
|
||||||
|
)
|
||||||
fun String.inContextOf(node: Node): String {
|
fun String.inContextOf(node: Node): String {
|
||||||
return if (this.isEmpty()) {
|
return if (this.isEmpty()) {
|
||||||
""
|
""
|
||||||
@@ -86,6 +103,11 @@ fun String.toAbsoluteUrl(domain: String): String = when {
|
|||||||
else -> this
|
else -> this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated(
|
||||||
|
message = "",
|
||||||
|
level = DeprecationLevel.ERROR,
|
||||||
|
replaceWith = ReplaceWith("attrAsRelativeUrl(attributeKey)"),
|
||||||
|
)
|
||||||
fun Element.relUrl(attributeKey: String): String {
|
fun Element.relUrl(attributeKey: String): String {
|
||||||
val attr = attr(attributeKey).trim()
|
val attr = attr(attributeKey).trim()
|
||||||
if (attr.isEmpty()) {
|
if (attr.isEmpty()) {
|
||||||
@@ -100,11 +122,12 @@ fun Element.relUrl(attributeKey: String): String {
|
|||||||
|
|
||||||
private val REGEX_URL_BASE = Regex("^[^/]{2,6}://[^/]+/", RegexOption.IGNORE_CASE)
|
private val REGEX_URL_BASE = Regex("^[^/]{2,6}://[^/]+/", RegexOption.IGNORE_CASE)
|
||||||
|
|
||||||
fun Element.css(property: String): String? {
|
@Deprecated(
|
||||||
val regex = Regex("${Regex.escape(property)}\\s*:\\s*[^;]+")
|
message = "",
|
||||||
val css = attr("style").find(regex) ?: return null
|
level = DeprecationLevel.ERROR,
|
||||||
return css.substringAfter(':').removeSuffix(';').trim()
|
replaceWith = ReplaceWith("styleValueOrNull(property)"),
|
||||||
}
|
)
|
||||||
|
fun Element.css(property: String): String? = styleValueOrNull(property)
|
||||||
|
|
||||||
fun DateFormat.tryParse(str: String?): Long = if (str.isNullOrEmpty()) {
|
fun DateFormat.tryParse(str: String?): Long = if (str.isNullOrEmpty()) {
|
||||||
0L
|
0L
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
@file:JvmName("StringUtils")
|
||||||
|
|
||||||
package org.koitharu.kotatsu.parsers.util
|
package org.koitharu.kotatsu.parsers.util
|
||||||
|
|
||||||
import androidx.collection.arraySetOf
|
import androidx.collection.arraySetOf
|
||||||
Reference in New Issue
Block a user