Minor daily update

devi 3 years ago
parent 9d33d1a560
commit 5e4dd82b8d

@ -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 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 key = it.attr("href").removeSuffix('/').substringAfterLast('/')
val name = it.selectFirst(".item_name")?.text() ?: it.text() val name = it.html().substringBefore("<")
MangaTag( MangaTag(
key = key, key = key,
title = name, 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 package org.koitharu.kotatsu.parsers.site.galleryadults.all
import org.jsoup.nodes.Element
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.* import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.site.galleryadults.GalleryAdultsParser import org.koitharu.kotatsu.parsers.site.galleryadults.GalleryAdultsParser
import org.koitharu.kotatsu.parsers.util.domain 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.oneOrThrowIfMany
import org.koitharu.kotatsu.parsers.util.parseHtml import org.koitharu.kotatsu.parsers.util.parseHtml
import org.koitharu.kotatsu.parsers.util.removeSuffix
import org.koitharu.kotatsu.parsers.util.urlEncoded import org.koitharu.kotatsu.parsers.util.urlEncoded
@MangaSourceParser("HENTAIENVY", "HentaiEnvy", type = ContentType.HENTAI) @MangaSourceParser("HENTAIENVY", "HentaiEnvy", type = ContentType.HENTAI)
@ -34,16 +31,6 @@ internal class HentaiEnvy(context: MangaLoaderContext) :
"/portuguese", "/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( override suspend fun getListPage(
page: Int, page: Int,
query: String?, query: String?,

@ -1,15 +1,11 @@
package org.koitharu.kotatsu.parsers.site.galleryadults.all package org.koitharu.kotatsu.parsers.site.galleryadults.all
import org.jsoup.nodes.Element
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.* import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.site.galleryadults.GalleryAdultsParser import org.koitharu.kotatsu.parsers.site.galleryadults.GalleryAdultsParser
import org.koitharu.kotatsu.parsers.util.domain import org.koitharu.kotatsu.parsers.util.*
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
@MangaSourceParser("HENTAIERA", "HentaiEra", type = ContentType.HENTAI) @MangaSourceParser("HENTAIERA", "HentaiEra", type = ContentType.HENTAI)
internal class HentaiEra(context: MangaLoaderContext) : internal class HentaiEra(context: MangaLoaderContext) :
@ -28,6 +24,16 @@ internal class HentaiEra(context: MangaLoaderContext) :
"/russian", "/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( override suspend fun getListPage(
page: Int, page: Int,
query: String?, query: String?,

@ -1,6 +1,5 @@
package org.koitharu.kotatsu.parsers.site.galleryadults.all package org.koitharu.kotatsu.parsers.site.galleryadults.all
import org.jsoup.nodes.Element
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.* import org.koitharu.kotatsu.parsers.model.*
@ -35,16 +34,6 @@ internal class HentaiForce(context: MangaLoaderContext) :
"/vietnamese", "/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 { override suspend fun getPageUrl(page: MangaPage): String {
val doc = webClient.httpGet(page.url.toAbsoluteUrl(domain)).parseHtml() val doc = webClient.httpGet(page.url.toAbsoluteUrl(domain)).parseHtml()
return doc.selectFirstOrThrow(idImg).src() ?: doc.parseFailed("Image src not found") return doc.selectFirstOrThrow(idImg).src() ?: doc.parseFailed("Image src not found")

@ -1,6 +1,5 @@
package org.koitharu.kotatsu.parsers.site.galleryadults.all package org.koitharu.kotatsu.parsers.site.galleryadults.all
import org.jsoup.nodes.Element
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.* import org.koitharu.kotatsu.parsers.model.*
@ -76,14 +75,4 @@ internal class HentaiFox(context: MangaLoaderContext) :
} }
return parseMangaList(webClient.httpGet(url).parseHtml()) 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 package org.koitharu.kotatsu.parsers.site.galleryadults.all
import org.jsoup.nodes.Element
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.* import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.site.galleryadults.GalleryAdultsParser 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) @MangaSourceParser("HENTAIROX", "HentaiRox", type = ContentType.HENTAI)
internal class HentaiRox(context: MangaLoaderContext) : internal class HentaiRox(context: MangaLoaderContext) :
@ -22,4 +25,14 @@ internal class HentaiRox(context: MangaLoaderContext) :
"/korean", "/korean",
"/german", "/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.site.galleryadults.GalleryAdultsParser
import org.koitharu.kotatsu.parsers.util.* import org.koitharu.kotatsu.parsers.util.*
@MangaSourceParser("NHENTAI", "NHentai", type = ContentType.HENTAI) @MangaSourceParser("NHENTAI", "NHentai.net", type = ContentType.HENTAI)
internal class NHentaiParser(context: MangaLoaderContext) : internal class NHentaiParser(context: MangaLoaderContext) :
GalleryAdultsParser(context, MangaSource.NHENTAI, "nhentai.net", 25) { GalleryAdultsParser(context, MangaSource.NHENTAI, "nhentai.net", 25) {
override val selectGallery = "div.index-container:not(.index-popular) .gallery, #related-container .gallery" 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.text.SimpleDateFormat
import java.util.* import java.util.*
@MangaSourceParser("DOUJINDESU", "DoujinDesu", "id") @MangaSourceParser("DOUJINDESU", "DoujinDesu.tv", "id")
class DoujinDesuParser(context: MangaLoaderContext) : PagedMangaParser(context, MangaSource.DOUJINDESU, pageSize = 18) { class DoujinDesuParser(context: MangaLoaderContext) : PagedMangaParser(context, MangaSource.DOUJINDESU, pageSize = 18) {
override val configKeyDomain: ConfigKey.Domain override val configKeyDomain: ConfigKey.Domain

@ -379,8 +379,8 @@ internal abstract class MadaraParser(
val doc = if (postReq) { val doc = if (postReq) {
val mangaId = document.select("div#manga-chapters-holder").attr("data-id") val mangaId = document.select("div#manga-chapters-holder").attr("data-id")
val url = "https://$domain/wp-admin/admin-ajax.php" val url = "https://$domain/wp-admin/admin-ajax.php"
val postdata = "action=manga_get_chapters&manga=$mangaId" val postData = "action=manga_get_chapters&manga=$mangaId"
webClient.httpPost(url, postdata).parseHtml() webClient.httpPost(url, postData).parseHtml()
} else { } else {
val url = mangaUrl.toAbsoluteUrl(domain).removeSuffix('/') + "/ajax/chapters/" val url = mangaUrl.toAbsoluteUrl(domain).removeSuffix('/') + "/ajax/chapters/"
webClient.httpPost(url, emptyMap()).parseHtml() 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.model.MangaSource
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
@MangaSourceParser("MANGALINK_AR", "MangaLink", "ar") @MangaSourceParser("MANGALINK_AR", "MangaLink.org", "ar")
internal class MangalinkParser(context: MangaLoaderContext) : internal class MangalinkParser(context: MangaLoaderContext) :
MadaraParser(context, MangaSource.MANGALINK_AR, "manga-link.org", pageSize = 10) { MadaraParser(context, MangaSource.MANGALINK_AR, "manga-link.org", pageSize = 10) {
override val listUrl = "readcomics/" 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 mangaId = document.select("div#manga-chapters-holder").attr("data-id")
val token = document.select("meta")[2].attr("content") val token = document.select("meta")[2].attr("content")
val url = "https://$domain/ajax" val url = "https://$domain/ajax"
val postdata = "_token=$token&action=manga_get_chapters&manga=$mangaId" val postData = "_token=$token&action=manga_get_chapters&manga=$mangaId"
val doc = webClient.httpPost(url, postdata).parseHtml() val doc = webClient.httpPost(url, postData).parseHtml()
val dateFormat = SimpleDateFormat(datePattern, sourceLocale) 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 mangaId = document.select("div#manga-chapters-holder").attr("data-id")
val url = "https://$domain/wp-admin/admin-ajax.php" val url = "https://$domain/wp-admin/admin-ajax.php"
val postdata = "action=manga_get_chapters&manga=$mangaId" val postData = "action=manga_get_chapters&manga=$mangaId"
val doc = webClient.httpPost(url, postdata).parseHtml() val doc = webClient.httpPost(url, postData).parseHtml()
val dateFormat = SimpleDateFormat(datePattern, sourceLocale) 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.model.MangaSource
import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser 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) : internal class DoujinDesuRip(context: MangaLoaderContext) :
MangaReaderParser(context, MangaSource.DOUJINDESURIP, "doujindesu.cfd", pageSize = 20, searchPageSize = 10) 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,
)
}
}
}
Loading…
Cancel
Save