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

Update domain + Add sources #1221

Merged
merged 23 commits into from
Nov 16, 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: 1152
total: 1154
153 changes: 153 additions & 0 deletions src/main/kotlin/org/koitharu/kotatsu/parsers/site/en/MyComicList.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
package org.koitharu.kotatsu.parsers.site.en

import androidx.collection.arraySetOf
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.network.UserAgents
import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.util.*
import java.text.SimpleDateFormat
import java.util.*

@MangaSourceParser("MYCOMICLIST", "MyComicList", "en", ContentType.COMICS)
internal class MyComicList(context: MangaLoaderContext) : PagedMangaParser(context, MangaParserSource.MYCOMICLIST, 24) {

override val configKeyDomain = ConfigKey.Domain("mycomiclist.org")

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

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

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

override suspend fun getFilterOptions() = MangaListFilterOptions(
availableTags = fetchAvailableTags(),
availableStates = EnumSet.of(MangaState.ONGOING, MangaState.FINISHED)
)

override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List<Manga> {
val url = buildString {
append("https://")
append(domain)
when {
!filter.query.isNullOrEmpty() -> {
append("/comic-search?key=")
append(filter.query.urlEncoded())
}
filter.tags.isNotEmpty() -> {
append("/")
append(filter.tags.first().key)
append("-comic")
}
else -> when (order) {
SortOrder.UPDATED -> append("/hot-comic")
SortOrder.POPULARITY -> append("/popular-comic")
SortOrder.NEWEST -> append("/new-comic")
else -> append("/ongoing-comic")
}
}
if (page > 1) {
append("?page=")
append(page)
}
}

val doc = webClient.httpGet(url).parseHtml()
return doc.select("div.manga-box").map { div ->
val href = div.selectFirstOrThrow("a").attrAsRelativeUrl("href")
val img = div.selectFirst("img.lazyload")
Manga(
id = generateUid(href),
url = href,
publicUrl = href.toAbsoluteUrl(domain),
title = div.selectFirst("h3 a")?.text().orEmpty(),
altTitle = null,
author = null,
tags = emptySet(),
rating = RATING_UNKNOWN,
isNsfw = isNsfwSource,
coverUrl = img?.attr("data-src").orEmpty(),
state = null,
source = source,
)
}
}

override suspend fun getDetails(manga: Manga): Manga {
val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml()
return manga.copy(
tags = doc.select("td:contains(Genres:) + td a").mapToSet { a ->
MangaTag(
key = a.attr("href").substringAfterLast('/').substringBefore("-comic"),
title = a.text().toTitleCase(sourceLocale),
source = source
)
},
author = doc.selectFirst("td:contains(Author:) + td")?.textOrNull(),
state = when(doc.selectFirst("td:contains(Status:) + td a")?.text()?.lowercase()) {
"ongoing" -> MangaState.ONGOING
"completed" -> MangaState.FINISHED
else -> null
},
description = doc.selectFirst("div.manga-desc p.pdesc")?.html(),
chapters = doc.select("ul.basic-list li").mapChapters(reversed = true) { i, li ->
val a = li.selectFirst("a.ch-name") ?: return@mapChapters null
val href = a.attrAsRelativeUrl("href")
val name = a.text()

MangaChapter(
id = generateUid(href),
name = name,
number = name.substringAfter('#').toFloatOrNull() ?: (i + 1f),
url = href,
scanlator = null,
uploadDate = 0L,
branch = null,
source = source,
volume = 0,
)
}
)
}

override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
val fullUrl = chapter.url.toAbsoluteUrl(domain) + "/all"
val doc = webClient.httpGet(fullUrl).parseHtml()

return doc.select("img.chapter_img.lazyload").mapNotNull { img ->
val imageUrl = img.attrOrNull("data-src") ?: return@mapNotNull null
MangaPage(
id = generateUid(imageUrl),
url = imageUrl,
preview = null,
source = source
)
}
}

private suspend fun fetchAvailableTags(): Set<MangaTag> {
val doc = webClient.httpGet("https://$domain").parseHtml()
return doc.select("div.cr-anime-box.genre-box a.genre-name").mapToSet { a ->
val href = a.attr("href")
val key = href.substringAfterLast('/').substringBefore("-comic")
MangaTag(
key = key,
title = a.text().toTitleCase(sourceLocale),
source = source
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ import org.koitharu.kotatsu.parsers.site.madara.MadaraParser

@MangaSourceParser("HARIMANGA", "HariManga", "en")
internal class HariManga(context: MangaLoaderContext) :
MadaraParser(context, MangaParserSource.HARIMANGA, "harimanga.com", pageSize = 10)
MadaraParser(context, MangaParserSource.HARIMANGA, "harimanga.me", pageSize = 10)
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,19 @@ 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.madara.MadaraParser
import org.koitharu.kotatsu.parsers.config.ConfigKey
import java.util.*
import kotlin.random.Random

@MangaSourceParser("MGKOMIK", "MgKomik", "id")
internal class Mgkomik(context: MangaLoaderContext) :
MadaraParser(context, MangaParserSource.MGKOMIK, "mgkomik.id", 20) {

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

override val tagPrefix = "genres/"
override val listUrl = "komik/"
override val datePattern = "dd MMM yy"
Expand Down
171 changes: 171 additions & 0 deletions src/main/kotlin/org/koitharu/kotatsu/parsers/site/vi/DuaLeoTruyen.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
package org.koitharu.kotatsu.parsers.site.vi

import org.json.JSONArray
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
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.network.UserAgents
import org.koitharu.kotatsu.parsers.util.*
import java.text.SimpleDateFormat
import java.util.*

@MangaSourceParser("DUALEOTRUYEN", "DuaLeoTruyen", "vi", type = ContentType.HENTAI)
internal class DuaLeoTruyen(context: MangaLoaderContext) :
PagedMangaParser(context, MangaParserSource.DUALEOTRUYEN, 60) {

override val configKeyDomain: ConfigKey.Domain
get() = ConfigKey.Domain("dualeotruyenomega.com")

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

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

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

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

override suspend fun getFilterOptions() = MangaListFilterOptions(
availableTags = fetchAvailableTags(),
)

override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List<Manga> {
val url = buildString {
append("https://")
append(domain)
when {
!filter.query.isNullOrEmpty() -> {
append("/tim-kiem.html")
append("?key=")
append(filter.query.urlEncoded())
}
filter.tags.isNotEmpty() -> {
append("/the-loai/")
append(filter.tags.first().key)
append(".html")
}
else -> when (order) {
SortOrder.POPULARITY -> append("/top-ngay.html")
else -> append("/truyen-moi-cap-nhat.html")
}
}
if (page > 1) {
append("?page=")
append(page)
}
}

val doc = webClient.httpGet(url).parseHtml()
return doc.select(".box_list > .li_truyen").map { li ->
val href = li.selectFirstOrThrow("a").attrAsRelativeUrl("href")
Manga(
id = generateUid(href),
title = li.selectFirst(".name")?.text().orEmpty(),
altTitle = null,
url = href,
publicUrl = href.toAbsoluteUrl(domain),
rating = RATING_UNKNOWN,
isNsfw = isNsfwSource,
coverUrl = li.selectFirst("img")?.absUrl("data-src").orEmpty(),
tags = emptySet(),
state = null,
author = null,
source = source
)
}
}

override suspend fun getDetails(manga: Manga): Manga {
val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml()
val dateFormat = SimpleDateFormat("dd/MM/yyyy", Locale.ENGLISH)

return manga.copy(
altTitle = doc.selectFirst(".box_info_right h2")?.textOrNull(),
tags = doc.select("ul.list-tag-story li a").mapToSet {
MangaTag(
key = it.attr("href").substringAfterLast('/').substringBefore('.'),
title = it.text().toTitleCase(sourceLocale),
source = source
)
},
state = when (doc.selectFirst(".info-item:has(.fa-rss)")?.text()?.removePrefix("Tình trang: ")) {
"Đang cập nhật" -> MangaState.ONGOING
"Full" -> MangaState.FINISHED
else -> null
},
author = doc.selectFirst(".info-item:has(.fa-user)")?.textOrNull()?.removePrefix("Tác giả: "),
description = doc.selectFirst(".story-detail-info")?.html(),
chapters = doc.select(".list-chapters .chapter-item").mapChapters(reversed = true) { i, div ->
val a = div.selectFirstOrThrow(".chap_name a")
val href = a.attrAsRelativeUrl("href")
val dateText = div.selectFirst(".chap_update")?.text()
MangaChapter(
id = generateUid(href),
name = a.text(),
number = i + 1f,
url = href,
scanlator = null,
uploadDate = dateFormat.tryParse(dateText),
branch = 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()

val chapterId = doc.selectFirst("input[name=chap]")?.`val`()
val comicsId = doc.selectFirst("input[name=truyen]")?.`val`()
if (chapterId != null && comicsId != null) {
webClient.httpPost(
url = "https://$domain/process.php",
form = mapOf(
"action" to "update_view_chap",
"truyen" to comicsId,
"chap" to chapterId
)
)
}

return doc.select(".content_view_chap img").mapIndexed { i, img ->
val url = img.absUrl("data-original")
MangaPage(
id = generateUid(url),
url = url,
preview = null,
source = source
)
}
}

private suspend fun fetchAvailableTags(): Set<MangaTag> {
return listOf(
"18+", "Đam Mỹ", "Harem", "Truyện Màu", "BoyLove", "GirlLove",
"Phiêu lưu", "Yaoi", "Hài Hước", "Bách Hợp", "Chuyển Sinh", "Drama",
"Hành Động", "Kịch Tính", "Cổ Đại", "Ecchi", "Hentai", "Lãng Mạn",
"Người Thú", "Tình Cảm", "Yuri", "Oneshot", "Doujinshi", "ABO"
).mapToSet { name ->
MangaTag(
key = name.lowercase().replace(' ', '-'),
title = name,
source = source
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -183,9 +183,10 @@ internal class XoxoComics(context: MangaLoaderContext) :
val doc = webClient.httpGet(fullUrl).parseHtml()
return doc.select(selectPage).mapNotNull { url ->
val img = url.src()?.toRelativeUrl(domain) ?: return@mapNotNull null
val originalImage = img.replace("[", "").replace("]", "")
MangaPage(
id = generateUid(img),
url = img,
url = originalImage,
preview = null,
source = source,
)
Expand Down