add source request
parent
7c4bffede2
commit
e67beae235
@ -0,0 +1,13 @@
|
||||
package org.koitharu.kotatsu.parsers.site.madara.pt
|
||||
|
||||
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("HIKARISCAN", "Hikari Scan", "pt")
|
||||
internal class HikariScan(context: MangaLoaderContext) :
|
||||
MadaraParser(context, MangaSource.HIKARISCAN, "hikariscan.com.br", pageSize = 10) {
|
||||
|
||||
override val datePattern: String = "dd 'de' MMMMM 'de' yyyy"
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
package org.koitharu.kotatsu.parsers.site.mangareader.ar
|
||||
|
||||
import org.koitharu.kotatsu.parsers.MangaLoaderContext
|
||||
import org.koitharu.kotatsu.parsers.MangaSourceParser
|
||||
import org.koitharu.kotatsu.parsers.config.ConfigKey
|
||||
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||
import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
|
||||
@MangaSourceParser("MANGAPROTM", "MangaProtm", "ar")
|
||||
internal class MangaProtm(context: MangaLoaderContext) :
|
||||
MangaReaderParser(context, MangaSource.MANGAPROTM, pageSize = 20, searchPageSize = 20) {
|
||||
|
||||
override val configKeyDomain: ConfigKey.Domain
|
||||
get() = ConfigKey.Domain("mangaprotm.com")
|
||||
|
||||
override val listUrl = "/series"
|
||||
override val chapterDateFormat: SimpleDateFormat = SimpleDateFormat("MMMM d, yyyy", Locale("ar", "AR"))
|
||||
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
package org.koitharu.kotatsu.parsers.site.mangareader.en
|
||||
|
||||
import org.koitharu.kotatsu.parsers.MangaLoaderContext
|
||||
import org.koitharu.kotatsu.parsers.MangaSourceParser
|
||||
import org.koitharu.kotatsu.parsers.config.ConfigKey
|
||||
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||
import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
|
||||
|
||||
@MangaSourceParser("REALMSCANS", "RealmScans", "en")
|
||||
internal class RealmScans(context: MangaLoaderContext) :
|
||||
MangaReaderParser(context, MangaSource.REALMSCANS, pageSize = 30, searchPageSize = 50) {
|
||||
override val configKeyDomain: ConfigKey.Domain
|
||||
get() = ConfigKey.Domain("realmscans.xyz")
|
||||
|
||||
override val listUrl = "/series"
|
||||
override val chapterDateFormat: SimpleDateFormat = SimpleDateFormat("dd MMM yyyy", Locale.ENGLISH)
|
||||
}
|
||||
@ -0,0 +1,216 @@
|
||||
package org.koitharu.kotatsu.parsers.site.mangareader.id
|
||||
|
||||
import org.json.JSONObject
|
||||
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.config.ConfigKey
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.parsers.model.MangaChapter
|
||||
import org.koitharu.kotatsu.parsers.model.MangaPage
|
||||
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||
import org.koitharu.kotatsu.parsers.model.MangaState
|
||||
import org.koitharu.kotatsu.parsers.model.RATING_UNKNOWN
|
||||
import org.koitharu.kotatsu.parsers.model.WordSet
|
||||
import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser
|
||||
import org.koitharu.kotatsu.parsers.util.attrAsAbsoluteUrl
|
||||
import org.koitharu.kotatsu.parsers.util.attrAsAbsoluteUrlOrNull
|
||||
import org.koitharu.kotatsu.parsers.util.attrAsRelativeUrl
|
||||
import org.koitharu.kotatsu.parsers.util.domain
|
||||
import org.koitharu.kotatsu.parsers.util.generateUid
|
||||
import org.koitharu.kotatsu.parsers.util.mapChapters
|
||||
import org.koitharu.kotatsu.parsers.util.mapNotNullToSet
|
||||
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.tryParse
|
||||
import java.text.DateFormat
|
||||
import java.util.Calendar
|
||||
|
||||
@MangaSourceParser("KOMIKCAST", "Komikcast", "id")
|
||||
internal class Komikcast(context: MangaLoaderContext) :
|
||||
MangaReaderParser(context, MangaSource.KOMIKCAST, pageSize = 60, searchPageSize = 28) {
|
||||
override val configKeyDomain: ConfigKey.Domain
|
||||
get() = ConfigKey.Domain("komikcast.io")
|
||||
|
||||
override val listUrl = "/daftar-komik"
|
||||
|
||||
override suspend fun getDetails(manga: Manga): Manga {
|
||||
val docs = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml()
|
||||
val chapters = docs.select("#chapter-wrapper > li").mapChapters(reversed = true) { index, element ->
|
||||
val url = element.selectFirst("a")?.attrAsRelativeUrl("href") ?: return@mapChapters null
|
||||
MangaChapter(
|
||||
id = generateUid(url),
|
||||
name = docs.selectFirst("a.chapter-link-item")?.ownText().orEmpty(),
|
||||
url = url,
|
||||
number = index + 1,
|
||||
scanlator = null,
|
||||
uploadDate = parseChapterDate(
|
||||
chapterDateFormat,
|
||||
element.selectFirst("div.chapter-link-time")?.text(),
|
||||
),
|
||||
branch = null,
|
||||
source = source,
|
||||
)
|
||||
}
|
||||
return parseInfo(docs, manga, chapters)
|
||||
}
|
||||
|
||||
override suspend fun parseInfo(docs: Document, manga: Manga, chapters: List<MangaChapter>): Manga {
|
||||
|
||||
/// set if is table
|
||||
|
||||
val tagMap = getOrCreateTagMap()
|
||||
|
||||
val tags = docs.select(".komik_info-content-genre > a").mapNotNullToSet { tagMap[it.text()] }
|
||||
|
||||
val state = docs.selectFirst(".komik_info-content-meta span:contains(Status)")?.lastElementChild()
|
||||
|
||||
val mangaState = state?.let {
|
||||
when (it.text()) {
|
||||
"Ongoing" -> MangaState.ONGOING
|
||||
"Completed" -> MangaState.FINISHED
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
val author = docs.selectFirst(".komik_info-content-meta span:contains(Author)")?.lastElementChild()?.text()
|
||||
|
||||
val nsfw = docs.selectFirst(".restrictcontainer") != null
|
||||
|| docs.selectFirst(".info-right .alr") != null
|
||||
|| docs.selectFirst(".postbody .alr") != null
|
||||
|
||||
return manga.copy(
|
||||
description = docs.selectFirst("div.komik_info-description-sinopsis")?.text(),
|
||||
state = mangaState,
|
||||
author = author,
|
||||
isNsfw = manga.isNsfw || nsfw,
|
||||
tags = tags,
|
||||
chapters = chapters,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
override fun parseMangaList(docs: Document): List<Manga> {
|
||||
return docs.select("div.list-update_item").mapNotNull {
|
||||
val a = it.selectFirst("a") ?: return@mapNotNull null
|
||||
val relativeUrl = a.attrAsRelativeUrl("href")
|
||||
val rating = it.selectFirst(".numscore")?.text()
|
||||
?.toFloatOrNull()?.div(10) ?: RATING_UNKNOWN
|
||||
|
||||
val name = it.selectFirst("h3.title")?.text().orEmpty()
|
||||
Manga(
|
||||
id = generateUid(relativeUrl),
|
||||
url = relativeUrl,
|
||||
title = name,
|
||||
altTitle = null,
|
||||
publicUrl = a.attrAsAbsoluteUrl("href"),
|
||||
rating = rating,
|
||||
isNsfw = isNsfwSource,
|
||||
coverUrl = it.selectFirst("img.ts-post-image")?.imageUrl().orEmpty(),
|
||||
tags = emptySet(),
|
||||
state = null,
|
||||
author = null,
|
||||
source = source,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun Element.imageUrl(): String {
|
||||
return attrAsAbsoluteUrlOrNull("src")
|
||||
?: attrAsAbsoluteUrlOrNull("data-src")
|
||||
?: attrAsAbsoluteUrlOrNull("data-cfsrc")
|
||||
?: ""
|
||||
}
|
||||
|
||||
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
|
||||
val chapterUrl = chapter.url.toAbsoluteUrl(domain)
|
||||
val docs = webClient.httpGet(chapterUrl).parseHtml()
|
||||
|
||||
val test = docs.select("script:containsData(ts_reader)")
|
||||
if (test.isNullOrEmpty()) {
|
||||
return docs.select("div#chapter_body img").map { img ->
|
||||
val url = img.imageUrl()
|
||||
MangaPage(
|
||||
id = generateUid(url),
|
||||
url = url,
|
||||
preview = null,
|
||||
source = source,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
val script = docs.selectFirstOrThrow("script:containsData(ts_reader)")
|
||||
val images = JSONObject(script.data().substringAfter('(').substringBeforeLast(')'))
|
||||
.getJSONArray("sources")
|
||||
.getJSONObject(0)
|
||||
.getJSONArray("images")
|
||||
val pages = ArrayList<MangaPage>(images.length())
|
||||
for (i in 0 until images.length()) {
|
||||
pages.add(
|
||||
MangaPage(
|
||||
id = generateUid(images.getString(i)),
|
||||
url = images.getString(i),
|
||||
preview = null,
|
||||
source = source,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
return pages
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected fun parseChapterDate(dateFormat: DateFormat, date: String?): Long {
|
||||
date ?: return 0
|
||||
return when {
|
||||
date.endsWith(" ago", ignoreCase = true) -> {
|
||||
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(
|
||||
"day",
|
||||
"days",
|
||||
).anyWordIn(date) -> cal.apply { add(Calendar.DAY_OF_MONTH, -number) }.timeInMillis
|
||||
|
||||
WordSet("hour", "hours").anyWordIn(date) -> cal.apply {
|
||||
add(
|
||||
Calendar.HOUR,
|
||||
-number,
|
||||
)
|
||||
}.timeInMillis
|
||||
|
||||
WordSet(
|
||||
"mins",
|
||||
).anyWordIn(date) -> cal.apply {
|
||||
add(
|
||||
Calendar.MINUTE,
|
||||
-number,
|
||||
)
|
||||
}.timeInMillis
|
||||
|
||||
WordSet("second").anyWordIn(date) -> cal.apply {
|
||||
add(
|
||||
Calendar.SECOND,
|
||||
-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
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue