Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new source + Tweak #1174

Merged
merged 11 commits into from
Oct 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/summary.yaml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
total: 1127
total: 1128
Original file line number Diff line number Diff line change
@@ -1,12 +1,48 @@
package org.koitharu.kotatsu.parsers.site.liliana.vi

import org.jsoup.Jsoup
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaChapter
import org.koitharu.kotatsu.parsers.model.MangaPage
import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.site.liliana.LilianaParser
import org.koitharu.kotatsu.parsers.Broken
import org.koitharu.kotatsu.parsers.util.*

@Broken
@MangaSourceParser("DOCTRUYEN5S", "DocTruyen5s", "vi")
internal class DocTruyen5s(context: MangaLoaderContext) :
LilianaParser(context, MangaParserSource.DOCTRUYEN5S, "dongmoe.com")
LilianaParser(context, MangaParserSource.DOCTRUYEN5S, "dongmoe.com") {

override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
val fullUrl = chapter.url.toAbsoluteUrl(domain)
val doc = webClient.httpGet(fullUrl).parseHtml()
val script = doc.selectFirstOrThrow("script:containsData(const CHAPTER_ID)")?.data()
val chapterId = script?.substringAfter("const CHAPTER_ID = ")?.substringBefore(';')
?: throw IllegalStateException("Không thể tìm thấy CHAPTER_ID, hãy kiểm tra nguồn")

val ajaxUrl = buildString {
append("https://")
append(domain)
append("/ajax/image/list/chap/")
append(chapterId)
}

val responseJson = webClient.httpGet(ajaxUrl).parseJson()

if (!responseJson.optBoolean("status", false)) {
throw IllegalStateException(responseJson.optString("msg"))
}

val pageListDoc = Jsoup.parse(responseJson.getString("html"))

return pageListDoc.select("div.separator a").map { element ->
val url = element.attr("href")
MangaPage(
id = generateUid(url),
url = url,
preview = null,
source = source
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,15 @@ import org.koitharu.kotatsu.parsers.model.MangaChapter
import org.koitharu.kotatsu.parsers.model.MangaPage
import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
import org.koitharu.kotatsu.parsers.config.ConfigKey
import org.koitharu.kotatsu.parsers.util.*

@MangaSourceParser("HENTAICUBE", "HentaiCube", "vi", ContentType.HENTAI)
internal class HentaiCube(context: MangaLoaderContext) :
MadaraParser(context, MangaParserSource.HENTAICUBE, "hentaicb.pro") {

override val configKeyDomain = ConfigKey.Domain("hentaicb.pro", "hentaicube.xyz")

override val datePattern = "dd/MM/yyyy"
override val tagPrefix = "the-loai/"
override val postReq = true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ internal class CuuTruyenParser(context: MangaLoaderContext) :
override val userAgentKey = ConfigKey.UserAgent(UserAgents.KOTATSU)

override val configKeyDomain = ConfigKey.Domain(
"cuutruyenpip7z.site",
"cuutruyen.net",
"nettrom.com",
"hetcuutruyen.net",
"cuutruyenpip7z.site",
"hetcuutruyen.net"
)

override fun onCreateConfig(keys: MutableCollection<ConfigKey<*>>) {
Expand Down
181 changes: 181 additions & 0 deletions src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/SayHentai.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
package org.koitharu.kotatsu.parsers.site.vi

import java.util.concurrent.atomic.AtomicReference
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.PagedMangaParser
import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.util.*
import org.koitharu.kotatsu.parsers.config.ConfigKey
import org.koitharu.kotatsu.parsers.network.UserAgents
import java.text.SimpleDateFormat
import java.util.*

@MangaSourceParser("SAYHENTAI", "SayHentai", "vi", ContentType.HENTAI)
internal class SayHentai(context: MangaLoaderContext) : PagedMangaParser(context, MangaParserSource.SAYHENTAI, 20) {
override val configKeyDomain = ConfigKey.Domain("sayhentai.one")

override fun onCreateConfig(keys: MutableCollection<ConfigKey<*>>) {
super.onCreateConfig(keys)
keys.add(userAgentKey)
}

private val tagsCache = AtomicReference<Set<MangaTag>?>(null)

override val availableSortOrders: Set<SortOrder> = EnumSet.of(
SortOrder.UPDATED,
SortOrder.POPULARITY,
SortOrder.ALPHABETICAL,
SortOrder.RATING
)

override val filterCapabilities: MangaListFilterCapabilities
get() = MangaListFilterCapabilities(
isSearchSupported = true,
)

override suspend fun getFilterOptions(): MangaListFilterOptions {
return MangaListFilterOptions(
availableStates = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED),
availableTags = fetchTags(),
)
}

override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List<Manga> {
val url = buildString {
append("https://")
append(domain)
if (!filter.query.isNullOrEmpty()) {
append("/search?s=")
append(filter.query.urlEncoded())
append("&page=")
append(page.toString())
} else {
if (filter.tags.isNotEmpty()) {
append("/genre/")
append(filter.tags.first().key)
append("/")
} else {
append("/")
}
append("?page=")
append(page.toString())
val sortQuery = getSortOrderQuery(order, filter.tags.isNotEmpty())
if (sortQuery.isNotEmpty()) {
append("&")
append(sortQuery)
}
}
}

val doc = webClient.httpGet(url).parseHtml()
return doc.select(".page-item-detail").mapNotNull { element ->
val href = element.selectFirst(".item-summary a")?.attrAsRelativeUrl("href") ?: return@mapNotNull null
Manga(
id = generateUid(href),
url = href,
publicUrl = href.toAbsoluteUrl(domain),
title = element.selectFirst(".item-summary a")?.text().orEmpty(),
coverUrl = element.selectFirst(".item-thumb img")?.src().orEmpty(),
altTitle = null,
rating = RATING_UNKNOWN,
tags = emptySet(),
author = null,
state = null,
source = source,
isNsfw = isNsfwSource
)
}
}

override suspend fun getDetails(manga: Manga): Manga {
val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml()
return manga.copy(
altTitle = doc.selectFirst("h2.other-name")?.text(),
author = doc.selectFirst("div.summary-heading:contains(Tác giả) + div.summary-content")?.text(),
tags = doc.select("div.genres-content a[rel=tag]").mapToSet { a ->
MangaTag(
key = a.attr("href").substringAfterLast('/'),
title = a.text().toTitleCase(sourceLocale),
source = source
)
},
description = doc.selectFirst("div.summary__content")?.html(),
state = when (doc.selectFirst("div.summary-heading:contains(Trạng thái) + div.summary-content")?.text()?.lowercase()) {
"đang ra" -> MangaState.ONGOING
"hoàn thành" -> MangaState.FINISHED
else -> null
},
chapters = doc.select("li.wp-manga-chapter").mapChapters(reversed = true) { i, element ->
val a = element.selectFirst("a") ?: return@mapChapters null
MangaChapter(
id = generateUid(a.attrAsRelativeUrl("href")),
name = a.text(),
number = i + 1f,
url = a.attrAsRelativeUrl("href"),
uploadDate = parseChapterDate(element.selectFirst("span.chapter-release-date")?.text()),
branch = null,
scanlator = null,
source = source,
volume = 0
)
}
)
}

override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
val fullUrl = chapter.url.toAbsoluteUrl(domain)
val doc = webClient.httpGet(fullUrl).parseHtml()
return doc.select("div.page-break img").mapIndexed { i, img ->
val url = img.src().orEmpty()
MangaPage(
id = generateUid(url),
url = url,
preview = null,
source = source
)
}
}

private fun parseChapterDate(date: String?): Long {
if (date == null) return 0
return when {
date.contains("giây trước") -> System.currentTimeMillis() - date.removeSuffix(" giây trước").toLong() * 1000
date.contains("phút trước") -> System.currentTimeMillis() - date.removeSuffix(" phút trước").toLong() * 60 * 1000
date.contains("giờ trước") -> System.currentTimeMillis() - date.removeSuffix(" giờ trước").toLong() * 60 * 60 * 1000
date.contains("ngày trước") -> System.currentTimeMillis() - date.removeSuffix(" ngày trước").toLong() * 24 * 60 * 60 * 1000
date.contains("tuần trước") -> System.currentTimeMillis() - date.removeSuffix(" tuần trước").toLong() * 7 * 24 * 60 * 60 * 1000
date.contains("tháng trước") -> System.currentTimeMillis() - date.removeSuffix(" tháng trước").toLong() * 30 * 24 * 60 * 60 * 1000
date.contains("năm trước") -> System.currentTimeMillis() - date.removeSuffix(" năm trước").toLong() * 365 * 24 * 60 * 60 * 1000
else -> SimpleDateFormat("dd/MM/yyyy", Locale.US).parse(date)?.time ?: 0L
}
}

private fun getSortOrderQuery(order: SortOrder, hasTags: Boolean): String {
if (!hasTags) return ""
return when (order) {
SortOrder.UPDATED -> "m_orderby=latest"
SortOrder.POPULARITY -> "m_orderby=views"
SortOrder.ALPHABETICAL -> "m_orderby=alphabet"
SortOrder.RATING -> "m_orderby=rating"
else -> "m_orderby=latest"
}
}

private suspend fun fetchTags(): Set<MangaTag> {
return tagsCache.get() ?: run {
val tags = webClient.httpGet("https://$domain/genre").parseHtml()
.select("ul.page-genres li a")
.mapToSet { a ->
val title = a.ownText().toTitleCase(sourceLocale)
MangaTag(
key = a.attr("href").substringAfterLast("/"),
title = title,
source = source
)
}
tagsCache.set(tags)
tags
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import java.text.SimpleDateFormat
@Broken
@MangaSourceParser("DOCTRUYEN3Q", "DocTruyen3Q", "vi")
internal class DocTruyen3Q(context: MangaLoaderContext) :
WpComicsParser(context, MangaParserSource.DOCTRUYEN3Q, "doctruyen3qmoi.pro", 36) {
WpComicsParser(context, MangaParserSource.DOCTRUYEN3Q, "doctruyen3qw.pro", 36) {

override val userAgentKey = ConfigKey.UserAgent(UserAgents.CHROME_DESKTOP)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import java.text.SimpleDateFormat
@Broken
@MangaSourceParser("TOPTRUYENVIET", "TopTruyen.pro", "vi")
internal class TopTruyenViet(context: MangaLoaderContext) :
WpComicsParser(context, MangaParserSource.TOPTRUYENVIET, "www.toptruyen68.pro", 36) {
WpComicsParser(context, MangaParserSource.TOPTRUYENVIET, "www.toptruyenww.pro", 36) {

override val userAgentKey = ConfigKey.UserAgent(UserAgents.CHROME_DESKTOP)

Expand Down
Loading