Merge pull request #1438 from AwkwardPeak7/weeb

[Weeb Central] Add source
master
Draken 1 year ago committed by GitHub
commit 6316ac055c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,341 @@
package org.koitharu.kotatsu.parsers.site.en
import kotlinx.coroutines.*
import okhttp3.HttpUrl.Companion.toHttpUrl
import org.jsoup.nodes.*
import org.koitharu.kotatsu.parsers.*
import org.koitharu.kotatsu.parsers.config.ConfigKey
import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.model.MangaState.*
import org.koitharu.kotatsu.parsers.model.ContentType.*
import org.koitharu.kotatsu.parsers.model.SortOrder.*
import org.koitharu.kotatsu.parsers.model.ContentRating.*
import org.koitharu.kotatsu.parsers.util.*
import java.text.SimpleDateFormat
import java.util.EnumSet
import java.util.Locale
@MangaSourceParser("WEEBCENTRAL", "Weeb Central", "en")
internal class WeebCentral(context: MangaLoaderContext) : MangaParser(context, MangaParserSource.WEEBCENTRAL),
MangaParserAuthProvider {
override val configKeyDomain = ConfigKey.Domain("weebcentral.com")
override val authUrl: String
get() = "https://$domain"
override val isAuthorized: Boolean
get() = context.cookieJar.getCookies(domain).any { it.name == "access_token" }
override suspend fun getUsername(): String {
return webClient.httpGet("https://$domain/users/me/profiles")
.parseHtml()
.selectFirstOrThrow("div:has(section > .avatar) .text-4xl")
.text()
}
override val availableSortOrders: Set<SortOrder> = EnumSet.of(
RELEVANCE,
ALPHABETICAL,
ALPHABETICAL_DESC,
POPULARITY,
POPULARITY_ASC,
RATING,
RATING_ASC,
ADDED,
ADDED_ASC,
UPDATED,
UPDATED_ASC,
)
override val filterCapabilities: MangaListFilterCapabilities =
MangaListFilterCapabilities(
isMultipleTagsSupported = true,
isTagsExclusionSupported = true,
isSearchSupported = true,
isSearchWithFiltersSupported = true,
)
override suspend fun getFilterOptions(): MangaListFilterOptions {
val document = webClient.httpGet("https://$domain/search")
.parseHtml()
val tags = document.select("section[x-show=show_filter] div:contains(tags) ~ fieldset label").mapToSet {
MangaTag(
title = it.selectFirstOrThrow(".label-text").text(),
key = it.selectFirstOrThrow("input[id$=value]").attr("value"),
source = source
)
}
val states = EnumSet.of(
ONGOING, FINISHED, ABANDONED, PAUSED
)
val types = EnumSet.of(
MANGA, MANHWA, MANHUA, COMICS
)
val rating = EnumSet.of(
SAFE, SUGGESTIVE
)
return MangaListFilterOptions(
availableTags = tags,
availableStates = states,
availableContentTypes = types,
availableContentRating = rating
)
}
override suspend fun getList(offset: Int, order: SortOrder, filter: MangaListFilter): List<Manga> {
val url = "https://$domain/search/data".toHttpUrl().newBuilder().apply {
addQueryParameter("limit", "32")
addQueryParameter("offset", offset.toString())
filter.query?.let {
val query = it
.replace(Regex("""[^a-zA-Z0-9\s]"""), " ")
.replace(Regex("""\s+"""), " ")
.trim()
addQueryParameter("text", query)
}
addQueryParameter(
name = "sort",
value = when (order) {
RELEVANCE -> "Best Match"
ALPHABETICAL, ALPHABETICAL_DESC -> "Alphabet"
POPULARITY, POPULARITY_ASC -> "Popularity"
RATING, RATING_ASC -> "Subscribers"
ADDED, ADDED_ASC -> "Recently Added"
UPDATED, UPDATED_ASC -> "Latest Updates"
else -> throw UnsupportedOperationException("unsupported order: $order")
}
)
addQueryParameter(
name = "order",
value = when (order) {
RELEVANCE, ALPHABETICAL, POPULARITY_ASC, RATING_ASC, ADDED_ASC, UPDATED_ASC -> "Ascending"
ALPHABETICAL_DESC, POPULARITY, RATING, ADDED, UPDATED -> "Descending"
else -> throw UnsupportedOperationException("unsupported order: $order")
}
)
addQueryParameter("official", "Any")
addQueryParameter("anime", "Any")
with (filter.contentRating) {
addQueryParameter(
name = "adult",
value = when {
isEmpty() -> "Any"
SAFE in this && SUGGESTIVE in this -> "Any"
SAFE in this -> "False"
SUGGESTIVE in this -> "True"
else -> throw UnsupportedOperationException("unsupported content rating: $this")
}
)
}
filter.states.forEach { state ->
addQueryParameter(
name = "included_status",
value = when (state) {
ONGOING -> "Ongoing"
FINISHED -> "Complete"
ABANDONED -> "Canceled"
PAUSED -> "Hiatus"
else -> throw UnsupportedOperationException("unsupported state: $state")
}
)
}
filter.types.forEach { type ->
addQueryParameter(
name = "included_type",
value = when (type) {
MANGA -> "Manga"
MANHWA -> "Manhwa"
MANHUA -> "Manhua"
COMICS -> "OEL"
else -> throw UnsupportedOperationException("unsupported type: $type")
}
)
}
filter.tags.forEach { tag ->
addQueryParameter("included_tag", tag.key)
}
filter.tagsExclude.forEach { tag ->
addQueryParameter("excluded_tag", tag.key)
}
addQueryParameter("display_mode", "Full Display")
}.build()
val document = webClient.httpGet(url).parseHtml()
return document.select("article:has(section)").map { element ->
val mangaId = element.selectFirstOrThrow("div > a")
.attrAsAbsoluteUrl("href")
.toHttpUrl()
.pathSegments[1]
Manga(
id = generateUid(mangaId),
url = mangaId,
publicUrl = "https://$domain/series/$mangaId",
title = element.selectFirstOrThrow("div > a").text(),
altTitle = null,
rating = RATING_UNKNOWN,
contentRating = if (element.selectFirst("svg:has(style:containsData(ff0000))") == null) {
SAFE
} else {
SUGGESTIVE
},
coverUrl = element.selectFirst("picture img")?.attrAsAbsoluteUrlOrNull("src"),
tags = element.selectFirst("div:contains(Tag(s): )")?.text()
?.substringAfter("Tag(s): ")
?.split(", ")
?.mapToSet {
MangaTag(
title = it,
key = it,
source = source
)
}
.orEmpty(),
state = when(document.selectFirst("div:contains(status) span")?.text()) {
"Ongoing" -> ONGOING
"Complete" -> FINISHED
"Canceled" -> ABANDONED
"Hiatus" -> PAUSED
else -> null
},
author = document.select("div:contains(author) a").eachText().joinToString(),
largeCoverUrl = null,
chapters = null,
source = source
)
}
}
override suspend fun getDetails(manga: Manga): Manga = coroutineScope {
val document = webClient.httpGet("https://$domain/series/${manga.url}")
.parseHtml()
val chapters = async { getChapters(manga.url, document) }
val sectionLeft = document.select("section[x-data] > section")[0]
val sectionRight = document.select("section[x-data] > section")[1]
manga.copy(
title = sectionRight.selectFirstOrThrow("h1").text(),
altTitle = sectionRight.select("li:has(strong:contains(Associated Name)) li")
.eachText().joinToString(),
publicUrl = "https://$domain/series/${manga.url}",
rating = RATING_UNKNOWN,
contentRating = if (sectionLeft.selectFirst("ul > li > strong:contains(Official Translation) + a:contains(Yes)") != null) {
SUGGESTIVE
} else {
SAFE
},
coverUrl = sectionLeft.selectFirst("img")?.attrAsAbsoluteUrlOrNull("src"),
tags = sectionRight.select("ul > li:has(strong:contains(Tag)) a").mapToSet {
MangaTag(
title = it.text(),
key = it.text(),
source = source
)
},
state = when (sectionLeft.selectFirst("ul > li:has(strong:contains(Status)) > a")?.text()) {
"Ongoing" -> ONGOING
"Complete" -> FINISHED
"Canceled" -> ABANDONED
"Hiatus" -> PAUSED
else -> null
},
author = sectionLeft.select("ul > li:has(strong:contains(Author)) > span > a")
.eachText().joinToString(),
description = Element("div").also { desc ->
sectionRight.selectFirst("li:has(strong:contains(Description)) > p")?.let {
desc.appendChild(it)
}
val ul = Element("ul")
sectionLeft.select("ul > li:has(strong:contains(Track)) abbr").stream().forEach { abbr ->
abbr.selectFirst("a")?.attr("href")?.let { url ->
val a = Element("a")
.text(
abbr.attr("title")
)
.attr("href", url)
ul.appendChild(
Element("li").appendChild(a)
)
}
}
if (ul.children().isNotEmpty()) {
desc.append("<br><strong>Links:</strong>")
desc.appendChild(ul)
}
}.outerHtml(),
chapters = chapters.await(),
source = source
)
}
private suspend fun getChapters(mangaId: String, mangaDocument: Document): List<MangaChapter> {
val document = if (mangaDocument.selectFirst("#chapter-list > button[hx-get*=full-chapter-list]") != null) {
webClient.httpGet("https://$domain/series/$mangaId/full-chapter-list").parseHtml()
} else {
mangaDocument
}
return document.select("div[x-data] > a").mapChapters(reversed = true) { _, element ->
val chapterId = element.attrAsAbsoluteUrl("href")
.toHttpUrl()
.pathSegments[1]
MangaChapter(
id = generateUid(chapterId),
url = chapterId,
name = element.selectFirstOrThrow("span.flex > span").text(),
number = element.selectFirstOrThrow("span.flex > span").text().let {
Regex("""(\d+(\.\d+)?)""").find(it)!!.groupValues[1].toFloat()
},
volume = 0,
scanlator = when (element.selectFirst("svg")?.attr("stroke")) {
"#d8b4fe" -> "Official"
"#4C4D54" -> "Unofficial"
else -> null
},
uploadDate = dateFormat.tryParse(
element.selectFirst("time[datetime]")?.attr("datetime")
),
branch = null,
source = source
)
}
}
private val dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.ENGLISH)
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
val url = "https://$domain".toHttpUrl().newBuilder().apply {
addPathSegment("chapters")
addPathSegment(chapter.url)
addPathSegment("images")
addQueryParameter("is_prev", "False")
addQueryParameter("reading_style", "long_strip")
}.build()
val document = webClient.httpGet(url).parseHtml()
return document.select("section[x-data~=scroll] > img").map { element ->
val pageUrl = element.attrAsAbsoluteUrl("src")
MangaPage(
id = generateUid(pageUrl),
url = pageUrl,
preview = null,
source = source
)
}
}
}

@ -1,10 +1,13 @@
package org.koitharu.kotatsu.parsers.site.nepnep.en
import org.koitharu.kotatsu.parsers.Broken
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.site.nepnep.NepnepParser
// site closed in favour of weeb central
@Broken
@MangaSourceParser("MANGA4LIFE", "Manga4Life", "en")
internal class Manga4Life(context: MangaLoaderContext) :
NepnepParser(context, MangaParserSource.MANGA4LIFE, "manga4life.com")

@ -1,10 +1,13 @@
package org.koitharu.kotatsu.parsers.site.nepnep.en
import org.koitharu.kotatsu.parsers.Broken
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.site.nepnep.NepnepParser
// site closed in favour of weeb central
@Broken
@MangaSourceParser("MANGASEE", "MangaSee", "en")
internal class MangaSee(context: MangaLoaderContext) :
NepnepParser(context, MangaParserSource.MANGASEE, "mangasee123.com")

Loading…
Cancel
Save