Minor daily update
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,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -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")
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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)
|
||||||
Loading…
Reference in New Issue