[SnowMTL] Add source (#1823)

Co-authored-by: Draken <dragonx943@users.noreply.github.com>
master
Draken 11 months ago committed by GitHub
parent ab26e6b3d0
commit e7fac812a7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -1 +1 @@
total: 1231
total: 1232

@ -0,0 +1,195 @@
package org.koitharu.kotatsu.parsers.site.en
import org.json.JSONArray
import org.json.JSONObject
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.model.search.MangaSearchQuery
import org.koitharu.kotatsu.parsers.model.search.MangaSearchQueryCapabilities
import org.koitharu.kotatsu.parsers.model.search.SearchCapability
import org.koitharu.kotatsu.parsers.model.search.SearchableField
import org.koitharu.kotatsu.parsers.model.search.QueryCriteria.*
import org.koitharu.kotatsu.parsers.util.*
import org.koitharu.kotatsu.parsers.core.PagedMangaParser
import org.koitharu.kotatsu.parsers.config.ConfigKey
import org.koitharu.kotatsu.parsers.exception.ParseException
import java.text.SimpleDateFormat
import java.util.*
@MangaSourceParser("SNOWMTL", "SnowMtl", "en", ContentType.OTHER)
internal class SnowMTL(context: MangaLoaderContext):
PagedMangaParser(context, MangaParserSource.SNOWMTL, 24) {
override val configKeyDomain = ConfigKey.Domain("snowmtl.ru")
override val availableSortOrders: Set<SortOrder> = EnumSet.of(
SortOrder.UPDATED,
SortOrder.POPULARITY,
SortOrder.NEWEST
)
override suspend fun getFilterOptions(): MangaListFilterOptions = MangaListFilterOptions()
override val searchQueryCapabilities = MangaSearchQueryCapabilities(
SearchCapability(
field = SearchableField.TITLE_NAME,
criteriaTypes = setOf(Match::class),
isMultiple = false
)
)
override suspend fun getListPage(query: MangaSearchQuery, page: Int): List<Manga> {
val url = buildString {
append("https://")
append(domain)
append("/search")
append("?")
when (query.order) {
SortOrder.POPULARITY -> append("sort_by=views")
SortOrder.UPDATED -> append("sort_by=recent")
else -> append("sort_by=recent")
}
if (page > 1) {
append("&page=")
append(page)
}
query.criteria.find { it.field == SearchableField.TITLE_NAME }?.let { criteria ->
when (criteria) {
is Match -> {
append("&q=")
append(criteria.value.toString())
}
is Include,
is Exclude,
is Range -> Unit // Not supported for this field
}
}
}
val doc = webClient.httpGet(url).parseHtml()
return doc.select("div.grid.grid-cols-1.sm\\:grid-cols-2.lg\\:grid-cols-3.xl\\:grid-cols-4.gap-8.p-6 > div").map { div ->
val href = div.selectFirst("a")?.attr("href") ?: throw ParseException("Link not found", url)
Manga(
id = generateUid(href),
url = href,
publicUrl = href.toAbsoluteUrl(domain),
coverUrl = div.selectFirst("a > div > img")?.src().orEmpty(),
title = div.selectFirst("div > a > h3")?.text().orEmpty(),
altTitles = emptySet(),
rating = RATING_UNKNOWN,
tags = emptySet(),
authors = emptySet(),
state = null,
source = source,
contentRating = null,
)
}
}
override suspend fun getDetails(manga: Manga): Manga {
val doc = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseHtml()
val altTitles = doc.select("p:contains(Alternative Title)").firstOrNull()?.text()
?.substringAfter("Alternative Title:")
?.removeSurrounding("[", "]")
?.split(",")
?.map { it.trim().removeSurrounding("'", "'") }
?.toSet()
?: emptySet()
val description = doc.select("p:contains(Synopsis)").firstOrNull()?.text()
?.substringAfter("Synopsis:").orEmpty()
val authors = doc.select("p:contains(Author:)").firstOrNull()?.text()
?.substringAfter("Author:")
val state = when (doc.select("p:contains(Status:)").firstOrNull()?.text()?.contains("Ongoing") == true) {
true -> MangaState.ONGOING
false -> MangaState.FINISHED
}
val chaptersRoot = doc.selectFirst("section.bg-gray-800.rounded-lg.shadow-md.mt-8.p-6")
?: throw ParseException("Chapters not found", manga.url)
val chapters = chaptersRoot.select("ul > li").mapNotNull { li ->
val link = li.selectFirst("a") ?: return@mapNotNull null
val href = link.attrAsRelativeUrl("href")
val title = link.text()
val number = title.substringAfter("Chapter ").substringBefore(" ").toFloatOrNull() ?: 0f
val dateString = li.select("span.text-gray-400").firstOrNull()?.text()?.trim() ?: ""
val uploadDate = if (dateString.isNotEmpty()) parseChapterDate(dateString) else 0L
MangaChapter(
id = generateUid(href),
title = title,
number = number,
volume = 0,
url = href,
scanlator = null,
uploadDate = uploadDate,
branch = null,
source = source
)
}.toList()
return manga.copy(
description = description,
authors = setOfNotNull(authors),
state = state,
altTitles = altTitles,
chapters = chapters.reversed()
)
}
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
val doc = webClient.httpGet(chapter.url.toAbsoluteUrl(domain)).parseHtml()
val jsonText = doc.selectFirst("div#json-data")?.text()
?: throw ParseException("JSON data not found", chapter.url)
val imgUrls = Regex(""""img_url":\s*"([^"]+)"""").findAll(jsonText)
.map { it.groupValues[1] }
.toList()
return imgUrls.map { imgUrl ->
val fullUrl = "https://$imgUrl"
MangaPage(
id = generateUid(fullUrl),
url = fullUrl,
preview = null,
source = source,
)
}
}
private fun parseChapterDate(dateString: String): Long {
val calendar = Calendar.getInstance()
return when {
"minute" in dateString -> {
val minutes = dateString.substringBefore(" minute").toInt()
calendar.add(Calendar.MINUTE, -minutes)
calendar.timeInMillis
}
"hour" in dateString -> {
val hours = dateString.substringBefore(" hour").toInt()
calendar.add(Calendar.HOUR_OF_DAY, -hours)
calendar.timeInMillis
}
"day" in dateString -> {
val days = dateString.substringBefore(" day").toInt()
calendar.add(Calendar.DAY_OF_YEAR, -days)
calendar.timeInMillis
}
else -> {
try {
val sdf = SimpleDateFormat("dd MMMM yyyy", Locale.ENGLISH)
sdf.timeZone = TimeZone.getTimeZone("UTC")
sdf.parse(dateString)?.time ?: 0L
} catch (e: Exception) {
0L
}
}
}
}
}
Loading…
Cancel
Save