Minor daily update
This commit is contained in:
@@ -0,0 +1,150 @@
|
||||
package org.koitharu.kotatsu.parsers.site.en
|
||||
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import org.koitharu.kotatsu.parsers.MangaLoaderContext
|
||||
import org.koitharu.kotatsu.parsers.MangaSourceParser
|
||||
import org.koitharu.kotatsu.parsers.PagedMangaParser
|
||||
import org.koitharu.kotatsu.parsers.config.ConfigKey
|
||||
import org.koitharu.kotatsu.parsers.model.*
|
||||
import org.koitharu.kotatsu.parsers.util.*
|
||||
import java.text.DateFormat
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
@MangaSourceParser("MANHWASMEN", "ManhwasMen", "en", type = ContentType.HENTAI)
|
||||
class ManhwasMen(context: MangaLoaderContext) :
|
||||
PagedMangaParser(context, MangaSource.MANHWASMEN, pageSize = 30, searchPageSize = 30) {
|
||||
|
||||
override val configKeyDomain: ConfigKey.Domain = ConfigKey.Domain("manhwas.men")
|
||||
|
||||
override val sortOrders: Set<SortOrder>
|
||||
get() = EnumSet.of(SortOrder.POPULARITY)
|
||||
|
||||
override suspend fun getListPage(
|
||||
page: Int,
|
||||
query: String?,
|
||||
tags: Set<MangaTag>?,
|
||||
sortOrder: SortOrder,
|
||||
): List<Manga> {
|
||||
val tag = tags.oneOrThrowIfMany()
|
||||
val url = buildString {
|
||||
append("https://")
|
||||
append(domain)
|
||||
append("/manga-list")
|
||||
append("?page=")
|
||||
append(page)
|
||||
when {
|
||||
!query.isNullOrEmpty() -> {
|
||||
append("&search=")
|
||||
append(query.urlEncoded())
|
||||
}
|
||||
|
||||
!tags.isNullOrEmpty() -> {
|
||||
append("&genero=")
|
||||
append(tag?.key.orEmpty())
|
||||
}
|
||||
}
|
||||
}
|
||||
val doc = webClient.httpGet(url).parseHtml()
|
||||
return doc.select("ul.animes li").map { li ->
|
||||
val href = li.selectFirstOrThrow("a").attrAsRelativeUrl("href")
|
||||
Manga(
|
||||
id = generateUid(href),
|
||||
url = href,
|
||||
publicUrl = href.toAbsoluteUrl(domain),
|
||||
coverUrl = li.selectFirst("img")?.src().orEmpty(),
|
||||
title = li.selectFirst(".title")?.text().orEmpty(),
|
||||
altTitle = null,
|
||||
rating = RATING_UNKNOWN,
|
||||
tags = emptySet(),
|
||||
author = null,
|
||||
state = null,
|
||||
source = source,
|
||||
isNsfw = isNsfwSource,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getTags(): Set<MangaTag> {
|
||||
val tags = webClient.httpGet("https://$domain/manga-list").parseHtml()
|
||||
.selectLastOrThrow(".filter-bx .form-group select.custom-select").select("option").drop(1)
|
||||
return tags.mapNotNullToSet { option ->
|
||||
MangaTag(
|
||||
key = option.attr("value").substringAfterLast("="),
|
||||
title = option.text(),
|
||||
source = source,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getDetails(manga: Manga): Manga = coroutineScope {
|
||||
val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml()
|
||||
manga.copy(
|
||||
tags = doc.body().select(".genres a").mapNotNullToSet { a ->
|
||||
MangaTag(
|
||||
key = a.attr("href").substringAfterLast('='),
|
||||
title = a.text(),
|
||||
source = source,
|
||||
)
|
||||
},
|
||||
description = doc.select(".sinopsis").html(),
|
||||
state = when (doc.selectLast(".anime-type-peli")?.text()?.lowercase()) {
|
||||
"ongoing" -> MangaState.ONGOING
|
||||
"completed" -> MangaState.FINISHED
|
||||
else -> null
|
||||
},
|
||||
chapters = doc.select(".episodes-list li").mapChapters(reversed = true) { i, li ->
|
||||
val url = li.selectFirstOrThrow("a").attrAsRelativeUrl("href")
|
||||
MangaChapter(
|
||||
id = generateUid(url),
|
||||
name = li.selectFirstOrThrow(".flex-grow-1 span").text(),
|
||||
number = i + 1,
|
||||
url = url,
|
||||
scanlator = null,
|
||||
uploadDate = parseChapterDate(
|
||||
SimpleDateFormat("dd/MM/yyyy", sourceLocale),
|
||||
li.selectLastOrThrow(".flex-grow-1 span").text(),
|
||||
),
|
||||
branch = null,
|
||||
source = source,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private fun parseChapterDate(dateFormat: DateFormat, date: String?): Long {
|
||||
val d = date?.lowercase() ?: return 0
|
||||
return when {
|
||||
d.endsWith(" ago") -> parseRelativeDate(date)
|
||||
else -> dateFormat.tryParse(date)
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseRelativeDate(date: String): Long {
|
||||
val number = Regex("""(\d+)""").find(date)?.value?.toIntOrNull() ?: return 0
|
||||
val cal = Calendar.getInstance()
|
||||
return when {
|
||||
WordSet("second").anyWordIn(date) -> cal.apply { add(Calendar.SECOND, -number) }.timeInMillis
|
||||
WordSet("minute", "minutes").anyWordIn(date) -> cal.apply { add(Calendar.MINUTE, -number) }.timeInMillis
|
||||
WordSet("hour", "hours").anyWordIn(date) -> cal.apply { add(Calendar.HOUR, -number) }.timeInMillis
|
||||
WordSet("day", "days").anyWordIn(date) -> cal.apply { add(Calendar.DAY_OF_MONTH, -number) }.timeInMillis
|
||||
WordSet("week", "weeks").anyWordIn(date) -> cal.apply { add(Calendar.WEEK_OF_YEAR, -number) }.timeInMillis
|
||||
WordSet("month", "months").anyWordIn(date) -> cal.apply { add(Calendar.MONTH, -number) }.timeInMillis
|
||||
WordSet("year").anyWordIn(date) -> cal.apply { add(Calendar.YEAR, -number) }.timeInMillis
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
|
||||
val doc = webClient.httpGet(chapter.url.toAbsoluteUrl(domain)).parseHtml().requireElementById("chapter_imgs")
|
||||
return doc.select("img").map { img ->
|
||||
val url = img.src()?.toRelativeUrl(domain) ?: img.parseFailed("Image src not found")
|
||||
MangaPage(
|
||||
id = generateUid(url),
|
||||
url = url,
|
||||
preview = null,
|
||||
source = source,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -130,9 +130,9 @@ internal abstract class GalleryAdultsParser(
|
||||
return root.parseTags() + tagLanguage
|
||||
}
|
||||
|
||||
protected open fun Element.parseTags() = select("a.tag, .gallery_title a").mapToSet {
|
||||
protected open fun Element.parseTags() = select("a").mapToSet {
|
||||
val key = it.attr("href").removeSuffix('/').substringAfterLast('/')
|
||||
val name = it.selectFirst(".item_name")?.text() ?: it.text()
|
||||
val name = it.html().substringBefore("<")
|
||||
MangaTag(
|
||||
key = key,
|
||||
title = name,
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
package org.koitharu.kotatsu.parsers.site.galleryadults.all
|
||||
|
||||
import org.jsoup.nodes.Document
|
||||
import org.koitharu.kotatsu.parsers.MangaLoaderContext
|
||||
import org.koitharu.kotatsu.parsers.MangaSourceParser
|
||||
import org.koitharu.kotatsu.parsers.model.*
|
||||
import org.koitharu.kotatsu.parsers.site.galleryadults.GalleryAdultsParser
|
||||
import org.koitharu.kotatsu.parsers.util.*
|
||||
|
||||
@MangaSourceParser("DOUJINDESUUK", "DoujinDesu.uk", type = ContentType.HENTAI)
|
||||
internal class DoujinDesuUk(context: MangaLoaderContext) :
|
||||
GalleryAdultsParser(context, MangaSource.DOUJINDESUUK, "doujindesu.uk", 50) {
|
||||
override val selectGallery = ".gallery"
|
||||
override val selectGalleryLink = "a"
|
||||
override val selectGalleryTitle = ".caption"
|
||||
override val pathTagUrl = "/tags?page="
|
||||
override val selectTags = "#tag-container"
|
||||
override val selectTag = "div.tag-container:contains(Tags) span.tags"
|
||||
override val selectAuthor = "div.tag-container:contains(Artists) a"
|
||||
override val selectLanguageChapter = "div.tag-container:contains(Languages) a"
|
||||
override val idImg = "image-container"
|
||||
override val listLanguage = arrayOf(
|
||||
"/english",
|
||||
"/japanese",
|
||||
"/chinese",
|
||||
)
|
||||
|
||||
override fun parseMangaList(doc: Document): List<Manga> {
|
||||
val regexBrackets = Regex("\\[[^]]+]|\\([^)]+\\)")
|
||||
val regexSpaces = Regex("\\s+")
|
||||
return doc.select(selectGallery).map { div ->
|
||||
val href = div.selectFirstOrThrow(selectGalleryLink).attrAsRelativeUrl("href")
|
||||
Manga(
|
||||
id = generateUid(href),
|
||||
title = div.select(selectGalleryTitle).text().replace(regexBrackets, "")
|
||||
.replace(regexSpaces, " ")
|
||||
.trim(),
|
||||
altTitle = null,
|
||||
url = href,
|
||||
publicUrl = href.toAbsoluteUrl(domain),
|
||||
rating = RATING_UNKNOWN,
|
||||
isNsfw = isNsfwSource,
|
||||
coverUrl = div.selectLastOrThrow(selectGalleryImg).src().orEmpty(),
|
||||
tags = emptySet(),
|
||||
state = null,
|
||||
author = null,
|
||||
source = source,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getPageUrl(page: MangaPage): String {
|
||||
val doc = webClient.httpGet(page.url.toAbsoluteUrl(domain)).parseHtml()
|
||||
val root = doc.body()
|
||||
return root.requireElementById(idImg).selectFirstOrThrow("img").src() ?: root.parseFailed("Image src not found")
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,15 +1,12 @@
|
||||
package org.koitharu.kotatsu.parsers.site.galleryadults.all
|
||||
|
||||
import org.jsoup.nodes.Element
|
||||
import org.koitharu.kotatsu.parsers.MangaLoaderContext
|
||||
import org.koitharu.kotatsu.parsers.MangaSourceParser
|
||||
import org.koitharu.kotatsu.parsers.model.*
|
||||
import org.koitharu.kotatsu.parsers.site.galleryadults.GalleryAdultsParser
|
||||
import org.koitharu.kotatsu.parsers.util.domain
|
||||
import org.koitharu.kotatsu.parsers.util.mapToSet
|
||||
import org.koitharu.kotatsu.parsers.util.oneOrThrowIfMany
|
||||
import org.koitharu.kotatsu.parsers.util.parseHtml
|
||||
import org.koitharu.kotatsu.parsers.util.removeSuffix
|
||||
import org.koitharu.kotatsu.parsers.util.urlEncoded
|
||||
|
||||
@MangaSourceParser("HENTAIENVY", "HentaiEnvy", type = ContentType.HENTAI)
|
||||
@@ -34,16 +31,6 @@ internal class HentaiEnvy(context: MangaLoaderContext) :
|
||||
"/portuguese",
|
||||
)
|
||||
|
||||
override fun Element.parseTags() = select("a").mapToSet {
|
||||
val key = it.attr("href").removeSuffix('/').substringAfterLast('/')
|
||||
val name = it.html().substringBefore("<")
|
||||
MangaTag(
|
||||
key = key,
|
||||
title = name,
|
||||
source = source,
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun getListPage(
|
||||
page: Int,
|
||||
query: String?,
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
package org.koitharu.kotatsu.parsers.site.galleryadults.all
|
||||
|
||||
import org.jsoup.nodes.Element
|
||||
import org.koitharu.kotatsu.parsers.MangaLoaderContext
|
||||
import org.koitharu.kotatsu.parsers.MangaSourceParser
|
||||
import org.koitharu.kotatsu.parsers.model.*
|
||||
import org.koitharu.kotatsu.parsers.site.galleryadults.GalleryAdultsParser
|
||||
import org.koitharu.kotatsu.parsers.util.domain
|
||||
import org.koitharu.kotatsu.parsers.util.oneOrThrowIfMany
|
||||
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
|
||||
import org.koitharu.kotatsu.parsers.util.*
|
||||
|
||||
@MangaSourceParser("HENTAIERA", "HentaiEra", type = ContentType.HENTAI)
|
||||
internal class HentaiEra(context: MangaLoaderContext) :
|
||||
@@ -28,6 +24,16 @@ internal class HentaiEra(context: MangaLoaderContext) :
|
||||
"/russian",
|
||||
)
|
||||
|
||||
override fun Element.parseTags() = select("a.tag, .gallery_title a").mapToSet {
|
||||
val key = it.attr("href").removeSuffix('/').substringAfterLast('/')
|
||||
val name = it.selectFirst(".item_name")?.text() ?: it.text()
|
||||
MangaTag(
|
||||
key = key,
|
||||
title = name,
|
||||
source = source,
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun getListPage(
|
||||
page: Int,
|
||||
query: String?,
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package org.koitharu.kotatsu.parsers.site.galleryadults.all
|
||||
|
||||
import org.jsoup.nodes.Element
|
||||
import org.koitharu.kotatsu.parsers.MangaLoaderContext
|
||||
import org.koitharu.kotatsu.parsers.MangaSourceParser
|
||||
import org.koitharu.kotatsu.parsers.model.*
|
||||
@@ -35,16 +34,6 @@ internal class HentaiForce(context: MangaLoaderContext) :
|
||||
"/vietnamese",
|
||||
)
|
||||
|
||||
override fun Element.parseTags() = select("a").mapToSet {
|
||||
val key = it.attr("href").removeSuffix('/').substringAfterLast('/')
|
||||
val name = it.html().substringBefore("<")
|
||||
MangaTag(
|
||||
key = key,
|
||||
title = name,
|
||||
source = source,
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun getPageUrl(page: MangaPage): String {
|
||||
val doc = webClient.httpGet(page.url.toAbsoluteUrl(domain)).parseHtml()
|
||||
return doc.selectFirstOrThrow(idImg).src() ?: doc.parseFailed("Image src not found")
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package org.koitharu.kotatsu.parsers.site.galleryadults.all
|
||||
|
||||
import org.jsoup.nodes.Element
|
||||
import org.koitharu.kotatsu.parsers.MangaLoaderContext
|
||||
import org.koitharu.kotatsu.parsers.MangaSourceParser
|
||||
import org.koitharu.kotatsu.parsers.model.*
|
||||
@@ -76,14 +75,4 @@ internal class HentaiFox(context: MangaLoaderContext) :
|
||||
}
|
||||
return parseMangaList(webClient.httpGet(url).parseHtml())
|
||||
}
|
||||
|
||||
override fun Element.parseTags() = select("a").mapToSet {
|
||||
val key = it.attr("href").removeSuffix('/').substringAfterLast('/')
|
||||
val name = it.html().substringBefore("<")
|
||||
MangaTag(
|
||||
key = key,
|
||||
title = name,
|
||||
source = source,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
package org.koitharu.kotatsu.parsers.site.galleryadults.all
|
||||
|
||||
import org.jsoup.nodes.Element
|
||||
import org.koitharu.kotatsu.parsers.MangaLoaderContext
|
||||
import org.koitharu.kotatsu.parsers.MangaSourceParser
|
||||
import org.koitharu.kotatsu.parsers.model.*
|
||||
import org.koitharu.kotatsu.parsers.site.galleryadults.GalleryAdultsParser
|
||||
import org.koitharu.kotatsu.parsers.util.mapToSet
|
||||
import org.koitharu.kotatsu.parsers.util.removeSuffix
|
||||
|
||||
@MangaSourceParser("HENTAIROX", "HentaiRox", type = ContentType.HENTAI)
|
||||
internal class HentaiRox(context: MangaLoaderContext) :
|
||||
@@ -22,4 +25,14 @@ internal class HentaiRox(context: MangaLoaderContext) :
|
||||
"/korean",
|
||||
"/german",
|
||||
)
|
||||
|
||||
override fun Element.parseTags() = select("a.tag, .gallery_title a").mapToSet {
|
||||
val key = it.attr("href").removeSuffix('/').substringAfterLast('/')
|
||||
val name = it.selectFirst(".item_name")?.text() ?: it.text()
|
||||
MangaTag(
|
||||
key = key,
|
||||
title = name,
|
||||
source = source,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import org.koitharu.kotatsu.parsers.model.*
|
||||
import org.koitharu.kotatsu.parsers.site.galleryadults.GalleryAdultsParser
|
||||
import org.koitharu.kotatsu.parsers.util.*
|
||||
|
||||
@MangaSourceParser("NHENTAI", "NHentai", type = ContentType.HENTAI)
|
||||
@MangaSourceParser("NHENTAI", "NHentai.net", type = ContentType.HENTAI)
|
||||
internal class NHentaiParser(context: MangaLoaderContext) :
|
||||
GalleryAdultsParser(context, MangaSource.NHENTAI, "nhentai.net", 25) {
|
||||
override val selectGallery = "div.index-container:not(.index-popular) .gallery, #related-container .gallery"
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
package org.koitharu.kotatsu.parsers.site.galleryadults.all
|
||||
|
||||
import org.koitharu.kotatsu.parsers.MangaLoaderContext
|
||||
import org.koitharu.kotatsu.parsers.MangaSourceParser
|
||||
import org.koitharu.kotatsu.parsers.model.*
|
||||
import org.koitharu.kotatsu.parsers.site.galleryadults.GalleryAdultsParser
|
||||
import org.koitharu.kotatsu.parsers.util.*
|
||||
import java.lang.IllegalArgumentException
|
||||
|
||||
@MangaSourceParser("NHENTAIUK", "NHentai.uk", type = ContentType.HENTAI)
|
||||
internal class NHentaiUk(context: MangaLoaderContext) :
|
||||
GalleryAdultsParser(context, MangaSource.NHENTAIUK, "nhentai.uk", 50) {
|
||||
override val selectGallery = ".gallery"
|
||||
override val selectGalleryLink = "a"
|
||||
override val selectGalleryTitle = ".caption"
|
||||
override val pathTagUrl = "/tags/popular?p="
|
||||
override val selectTags = "#tag-container"
|
||||
override val selectTag = "div.tag-container:contains(Tags:) span.tags"
|
||||
override val selectAuthor = "div.tag-container:contains(Artists:) a"
|
||||
override val selectLanguageChapter = "div.tag-container:contains(Languages:) a"
|
||||
override val idImg = "image-container"
|
||||
override val listLanguage = arrayOf(
|
||||
"/english",
|
||||
"/french",
|
||||
"/japanese",
|
||||
"/chinese",
|
||||
"/spanish",
|
||||
"/russian",
|
||||
"/korean",
|
||||
"/german",
|
||||
"/italian",
|
||||
"/portuguese",
|
||||
"/turkish",
|
||||
)
|
||||
|
||||
override suspend fun getListPage(
|
||||
page: Int,
|
||||
query: String?,
|
||||
tags: Set<MangaTag>?,
|
||||
sortOrder: SortOrder,
|
||||
): List<Manga> {
|
||||
val tag = tags.oneOrThrowIfMany()
|
||||
val url = buildString {
|
||||
append("https://")
|
||||
append(domain)
|
||||
if (!tags.isNullOrEmpty()) {
|
||||
if (tag?.key == "languageKey") {
|
||||
append("/language")
|
||||
append(tag.title)
|
||||
append("/?p=")
|
||||
} else {
|
||||
append("/tag/")
|
||||
append(tag?.key)
|
||||
append("/?p=")
|
||||
}
|
||||
} else if (!query.isNullOrEmpty()) {
|
||||
throw IllegalArgumentException("Search is not supported by this source")
|
||||
} else {
|
||||
append("/home?p=")
|
||||
}
|
||||
append(page)
|
||||
}
|
||||
return parseMangaList(webClient.httpGet(url).parseHtml())
|
||||
}
|
||||
|
||||
override suspend fun getPageUrl(page: MangaPage): String {
|
||||
val doc = webClient.httpGet(page.url.toAbsoluteUrl(domain)).parseHtml()
|
||||
val root = doc.body()
|
||||
return root.requireElementById(idImg).selectFirstOrThrow("img").src() ?: root.parseFailed("Image src not found")
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ import org.koitharu.kotatsu.parsers.util.*
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
@MangaSourceParser("DOUJINDESU", "DoujinDesu", "id")
|
||||
@MangaSourceParser("DOUJINDESU", "DoujinDesu.tv", "id")
|
||||
class DoujinDesuParser(context: MangaLoaderContext) : PagedMangaParser(context, MangaSource.DOUJINDESU, pageSize = 18) {
|
||||
|
||||
override val configKeyDomain: ConfigKey.Domain
|
||||
|
||||
@@ -379,8 +379,8 @@ internal abstract class MadaraParser(
|
||||
val doc = if (postReq) {
|
||||
val mangaId = document.select("div#manga-chapters-holder").attr("data-id")
|
||||
val url = "https://$domain/wp-admin/admin-ajax.php"
|
||||
val postdata = "action=manga_get_chapters&manga=$mangaId"
|
||||
webClient.httpPost(url, postdata).parseHtml()
|
||||
val postData = "action=manga_get_chapters&manga=$mangaId"
|
||||
webClient.httpPost(url, postData).parseHtml()
|
||||
} else {
|
||||
val url = mangaUrl.toAbsoluteUrl(domain).removeSuffix('/') + "/ajax/chapters/"
|
||||
webClient.httpPost(url, emptyMap()).parseHtml()
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package org.koitharu.kotatsu.parsers.site.madara.ar
|
||||
|
||||
import org.koitharu.kotatsu.parsers.MangaLoaderContext
|
||||
import org.koitharu.kotatsu.parsers.MangaSourceParser
|
||||
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
|
||||
|
||||
@MangaSourceParser("MANGALINKNET", "MangaLink.net", "ar")
|
||||
internal class MangalinkNet(context: MangaLoaderContext) :
|
||||
MadaraParser(context, MangaSource.MANGALINKNET, "manga-link.net", pageSize = 10)
|
||||
@@ -5,7 +5,7 @@ import org.koitharu.kotatsu.parsers.MangaSourceParser
|
||||
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
|
||||
|
||||
@MangaSourceParser("MANGALINK_AR", "MangaLink", "ar")
|
||||
@MangaSourceParser("MANGALINK_AR", "MangaLink.org", "ar")
|
||||
internal class MangalinkParser(context: MangaLoaderContext) :
|
||||
MadaraParser(context, MangaSource.MANGALINK_AR, "manga-link.org", pageSize = 10) {
|
||||
override val listUrl = "readcomics/"
|
||||
|
||||
@@ -105,8 +105,8 @@ internal class InstaManhwa(context: MangaLoaderContext) :
|
||||
val mangaId = document.select("div#manga-chapters-holder").attr("data-id")
|
||||
val token = document.select("meta")[2].attr("content")
|
||||
val url = "https://$domain/ajax"
|
||||
val postdata = "_token=$token&action=manga_get_chapters&manga=$mangaId"
|
||||
val doc = webClient.httpPost(url, postdata).parseHtml()
|
||||
val postData = "_token=$token&action=manga_get_chapters&manga=$mangaId"
|
||||
val doc = webClient.httpPost(url, postData).parseHtml()
|
||||
|
||||
val dateFormat = SimpleDateFormat(datePattern, sourceLocale)
|
||||
|
||||
|
||||
@@ -19,8 +19,8 @@ internal class ManhwaTop(context: MangaLoaderContext) :
|
||||
|
||||
val mangaId = document.select("div#manga-chapters-holder").attr("data-id")
|
||||
val url = "https://$domain/wp-admin/admin-ajax.php"
|
||||
val postdata = "action=manga_get_chapters&manga=$mangaId"
|
||||
val doc = webClient.httpPost(url, postdata).parseHtml()
|
||||
val postData = "action=manga_get_chapters&manga=$mangaId"
|
||||
val doc = webClient.httpPost(url, postData).parseHtml()
|
||||
|
||||
val dateFormat = SimpleDateFormat(datePattern, sourceLocale)
|
||||
|
||||
|
||||
@@ -6,6 +6,6 @@ import org.koitharu.kotatsu.parsers.model.ContentType
|
||||
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||
import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser
|
||||
|
||||
@MangaSourceParser("DOUJINDESURIP", "DoujinDesuRip", "id", ContentType.HENTAI)
|
||||
@MangaSourceParser("DOUJINDESURIP", "DoujinDesu.cfd", "id", ContentType.HENTAI)
|
||||
internal class DoujinDesuRip(context: MangaLoaderContext) :
|
||||
MangaReaderParser(context, MangaSource.DOUJINDESURIP, "doujindesu.cfd", pageSize = 20, searchPageSize = 10)
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
package org.koitharu.kotatsu.parsers.site.tr
|
||||
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import org.koitharu.kotatsu.parsers.MangaLoaderContext
|
||||
import org.koitharu.kotatsu.parsers.MangaSourceParser
|
||||
import org.koitharu.kotatsu.parsers.PagedMangaParser
|
||||
import org.koitharu.kotatsu.parsers.config.ConfigKey
|
||||
import org.koitharu.kotatsu.parsers.model.*
|
||||
import org.koitharu.kotatsu.parsers.util.*
|
||||
import java.text.DateFormat
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
@MangaSourceParser("TRWEBTOON", "TrWebtoon", "tr")
|
||||
class TrWebtoon(context: MangaLoaderContext) :
|
||||
PagedMangaParser(context, MangaSource.TRWEBTOON, pageSize = 21) {
|
||||
|
||||
override val configKeyDomain: ConfigKey.Domain = ConfigKey.Domain("trwebtoon.com")
|
||||
|
||||
override val sortOrders: Set<SortOrder>
|
||||
get() = EnumSet.of(SortOrder.POPULARITY, SortOrder.ALPHABETICAL)
|
||||
|
||||
override suspend fun getListPage(
|
||||
page: Int,
|
||||
query: String?,
|
||||
tags: Set<MangaTag>?,
|
||||
sortOrder: SortOrder,
|
||||
): List<Manga> {
|
||||
val tag = tags.oneOrThrowIfMany()
|
||||
val url = buildString {
|
||||
append("https://")
|
||||
append(domain)
|
||||
append("/webtoon-listesi")
|
||||
append("?page=")
|
||||
append(page)
|
||||
when {
|
||||
!query.isNullOrEmpty() -> {
|
||||
append("&q=")
|
||||
append(query.urlEncoded())
|
||||
}
|
||||
|
||||
!tags.isNullOrEmpty() -> {
|
||||
append("&genre=")
|
||||
append(tag?.key.orEmpty())
|
||||
}
|
||||
}
|
||||
append("&sort=")
|
||||
when (sortOrder) {
|
||||
SortOrder.POPULARITY -> append("views&short_type=DESC")
|
||||
SortOrder.ALPHABETICAL -> append("name&short_type=ASC")
|
||||
else -> append("views&short_type=DESC")
|
||||
}
|
||||
}
|
||||
val doc = webClient.httpGet(url).parseHtml()
|
||||
return doc.select(".row .col-xl-4 .card-body").map { li ->
|
||||
val href = li.selectFirstOrThrow("a").attrAsRelativeUrl("href")
|
||||
Manga(
|
||||
id = generateUid(href),
|
||||
url = href,
|
||||
publicUrl = href.toAbsoluteUrl(domain),
|
||||
coverUrl = li.selectFirst("img")?.src().orEmpty(),
|
||||
title = li.selectFirst(".table-responsive a")?.text().orEmpty(),
|
||||
altTitle = null,
|
||||
rating = li.selectFirst(".row .col-xl-4 .mt-2 .my-1 .text-muted")?.text()?.substringBefore("/")
|
||||
?.toFloatOrNull()?.div(5f) ?: RATING_UNKNOWN,
|
||||
tags = emptySet(),
|
||||
author = null,
|
||||
state = when (doc.selectLast(".row .col-xl-4 .mt-2 .rounded-pill")?.text()) {
|
||||
"Devam Ediyor", "Güncel" -> MangaState.ONGOING
|
||||
"Tamamlandı" -> MangaState.FINISHED
|
||||
else -> null
|
||||
},
|
||||
source = source,
|
||||
isNsfw = isNsfwSource,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getTags(): Set<MangaTag> {
|
||||
val tags =
|
||||
webClient.httpGet("https://$domain/webtoon-listesi").parseHtml().requireElementById("collapseExample")
|
||||
.select(".pt-12 a").drop(1)
|
||||
return tags.mapNotNullToSet { a ->
|
||||
MangaTag(
|
||||
key = a.attr("href").substringAfterLast("genre=").substringBefore("&sort"),
|
||||
title = a.text(),
|
||||
source = source,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getDetails(manga: Manga): Manga = coroutineScope {
|
||||
val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml()
|
||||
manga.copy(
|
||||
tags = doc.body().select("li.movie__year a").mapNotNullToSet { a ->
|
||||
MangaTag(
|
||||
key = a.attr("href").substringAfterLast('='),
|
||||
title = a.text(),
|
||||
source = source,
|
||||
)
|
||||
},
|
||||
description = doc.select("p.movie__plot").html(),
|
||||
state = when (doc.selectFirstOrThrow(".movie__credits span.rounded-pill").text()) {
|
||||
"Devam Ediyor", "Güncel" -> MangaState.ONGOING
|
||||
"Tamamlandı" -> MangaState.FINISHED
|
||||
else -> null
|
||||
},
|
||||
chapters = doc.requireElementById("chapters").select("tbody tr").mapChapters(reversed = true) { i, tr ->
|
||||
val url = tr.selectFirstOrThrow("a").attrAsRelativeUrl("href")
|
||||
MangaChapter(
|
||||
id = generateUid(url),
|
||||
name = tr.selectFirstOrThrow("a").text(),
|
||||
number = i + 1,
|
||||
url = url,
|
||||
scanlator = null,
|
||||
uploadDate = parseChapterDate(
|
||||
SimpleDateFormat("dd/MM/yyyy", sourceLocale),
|
||||
tr.selectLastOrThrow("td").selectFirstOrThrow("span").text(),
|
||||
),
|
||||
branch = null,
|
||||
source = source,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private fun parseChapterDate(dateFormat: DateFormat, date: String?): Long {
|
||||
val d = date?.lowercase() ?: return 0
|
||||
return when {
|
||||
d.startsWith("saat ") -> Calendar.getInstance().apply {
|
||||
set(Calendar.HOUR_OF_DAY, 0)
|
||||
set(Calendar.MINUTE, 0)
|
||||
set(Calendar.SECOND, 0)
|
||||
set(Calendar.MILLISECOND, 0)
|
||||
}.timeInMillis
|
||||
|
||||
d.endsWith(" önce") -> parseRelativeDate(date)
|
||||
|
||||
else -> dateFormat.tryParse(date)
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseRelativeDate(date: String): Long {
|
||||
val number = Regex("""(\d+)""").find(date)?.value?.toIntOrNull() ?: return 0
|
||||
val cal = Calendar.getInstance()
|
||||
return when {
|
||||
WordSet("saat").anyWordIn(date) -> cal.apply { add(Calendar.HOUR, -number) }.timeInMillis
|
||||
WordSet("gün").anyWordIn(date) -> cal.apply { add(Calendar.DAY_OF_MONTH, -number) }.timeInMillis
|
||||
WordSet("hafta").anyWordIn(date) -> cal.apply { add(Calendar.WEEK_OF_YEAR, -number) }.timeInMillis
|
||||
WordSet("ay").anyWordIn(date) -> cal.apply { add(Calendar.MONTH, -number) }.timeInMillis
|
||||
WordSet("yıl").anyWordIn(date) -> cal.apply { add(Calendar.YEAR, -number) }.timeInMillis
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
|
||||
val doc = webClient.httpGet(chapter.url.toAbsoluteUrl(domain)).parseHtml().requireElementById("images")
|
||||
return doc.select("img").map { img ->
|
||||
val url = img.src()?.toRelativeUrl(domain) ?: img.parseFailed("Image src not found")
|
||||
MangaPage(
|
||||
id = generateUid(url),
|
||||
url = url,
|
||||
preview = null,
|
||||
source = source,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user