Tachiyomi-Extensions/multisrc/overrides/mangathemesia/komikcast/src/KomikCast.kt

254 lines
10 KiB
Kotlin
Raw Normal View History

package eu.kanade.tachiyomi.extension.id.komikcast
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.interceptor.rateLimit
import eu.kanade.tachiyomi.source.model.Filter
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient
import okhttp3.Request
import org.jsoup.Jsoup
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import java.util.Calendar
import java.util.Locale
class KomikCast : MangaThemesia("Komik Cast", "https://komikcast.lol", "id", "/daftar-komik") {
// Formerly "Komik Cast (WP Manga Stream)"
override val id = 972717448578983812
Random User-Agent Refactor (#17059) * lib-randomua * NHentai: Random mobile ua * Madara random UA overhaul * MangaThemesia random UA overhaul * MangaHub random UA overhaul * build errors and warnings * remove preference from Constellar * change to singleton object * network.client * fix copy paste and chapter deep link * exit early * use data class and enum options * missing import * suggested changes Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com> * re-add empty check to filters * convert to interceptor * update comment Co-authored-by: Alessandro Jean <14254807+alessandrojean@users.noreply.github.com> * update error message * initialize client by lazy * dont check on excluded Filters Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com> * newlines * move preference helper function into lib * move preference helper function into lib x2 * move check to lib too * move defaultRandomUserAgentType to constructor * rename the interceptor * organize the interceptor and preference stuff in different files * hide custom ua setting when random ua is enabled * English Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com> * catch specific exception Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com> * setVisible() fresh stuff * setVisible() fresh stuff * change summary * workaround * Update error message Co-authored-by: Alessandro Jean <14254807+alessandrojean@users.noreply.github.com> * Update comment Co-authored-by: Alessandro Jean <14254807+alessandrojean@users.noreply.github.com> --------- Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Co-authored-by: Alessandro Jean <14254807+alessandrojean@users.noreply.github.com>
2023-07-15 22:52:35 +00:00
override val client: OkHttpClient = super.client.newBuilder()
.rateLimit(3)
.build()
Random User-Agent Refactor (#17059) * lib-randomua * NHentai: Random mobile ua * Madara random UA overhaul * MangaThemesia random UA overhaul * MangaHub random UA overhaul * build errors and warnings * remove preference from Constellar * change to singleton object * network.client * fix copy paste and chapter deep link * exit early * use data class and enum options * missing import * suggested changes Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com> * re-add empty check to filters * convert to interceptor * update comment Co-authored-by: Alessandro Jean <14254807+alessandrojean@users.noreply.github.com> * update error message * initialize client by lazy * dont check on excluded Filters Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com> * newlines * move preference helper function into lib * move preference helper function into lib x2 * move check to lib too * move defaultRandomUserAgentType to constructor * rename the interceptor * organize the interceptor and preference stuff in different files * hide custom ua setting when random ua is enabled * English Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com> * catch specific exception Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com> * setVisible() fresh stuff * setVisible() fresh stuff * change summary * workaround * Update error message Co-authored-by: Alessandro Jean <14254807+alessandrojean@users.noreply.github.com> * Update comment Co-authored-by: Alessandro Jean <14254807+alessandrojean@users.noreply.github.com> --------- Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Co-authored-by: Alessandro Jean <14254807+alessandrojean@users.noreply.github.com>
2023-07-15 22:52:35 +00:00
override fun headersBuilder(): Headers.Builder = super.headersBuilder()
.add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9")
.add("Accept-language", "en-US,en;q=0.9,id;q=0.8")
override fun imageRequest(page: Page): Request {
val newHeaders = headersBuilder()
.set("Accept", "image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8")
.set("Referer", "$baseUrl/")
.build()
return GET(page.imageUrl!!, newHeaders)
}
override fun popularMangaRequest(page: Int) = customPageRequest(page, "orderby", "popular")
override fun latestUpdatesRequest(page: Int) = customPageRequest(page, "sortby", "update")
private fun customPageRequest(page: Int, filterKey: String, filterValue: String): Request {
val pagePath = if (page > 1) "page/$page/" else ""
return GET("$baseUrl$mangaUrlDirectory/$pagePath?$filterKey=$filterValue", headers)
}
override fun searchMangaSelector() = "div.list-update_item"
override fun searchMangaFromElement(element: Element) = super.searchMangaFromElement(element).apply {
2023-02-12 03:22:32 +00:00
title = element.selectFirst("h3.title")!!.ownText()
}
override val seriesDetailsSelector = "div.komik_info:has(.komik_info-content)"
override val seriesTitleSelector = "h1.komik_info-content-body-title"
override val seriesDescriptionSelector = ".komik_info-description-sinopsis"
override val seriesAltNameSelector = ".komik_info-content-native"
override val seriesGenreSelector = ".komik_info-content-genre a"
override val seriesThumbnailSelector = ".komik_info-content-thumbnail img"
override val seriesStatusSelector = ".komik_info-content-info:contains(Status)"
override fun mangaDetailsParse(document: Document) = SManga.create().apply {
document.selectFirst(seriesDetailsSelector)?.let { seriesDetails ->
title = seriesDetails.selectFirst(seriesTitleSelector)?.text()
?.replace("bahasa indonesia", "", ignoreCase = true)?.trim().orEmpty()
artist = seriesDetails.selectFirst(seriesArtistSelector)?.ownText().removeEmptyPlaceholder()
author = seriesDetails.selectFirst(seriesAuthorSelector)?.ownText().removeEmptyPlaceholder()
description = seriesDetails.select(seriesDescriptionSelector).joinToString("\n") { it.text() }.trim()
// Add alternative name to manga description
val altName = seriesDetails.selectFirst(seriesAltNameSelector)?.ownText().takeIf { it.isNullOrBlank().not() }
altName?.let {
description = "$description\n\n$altNamePrefix$altName".trim()
}
val genres = seriesDetails.select(seriesGenreSelector).map { it.text() }.toMutableList()
// Add series type (manga/manhwa/manhua/other) to genre
seriesDetails.selectFirst(seriesTypeSelector)?.ownText().takeIf { it.isNullOrBlank().not() }?.let { genres.add(it) }
genre = genres.map { genre ->
genre.lowercase(Locale.forLanguageTag(lang)).replaceFirstChar { char ->
if (char.isLowerCase()) {
char.titlecase(Locale.forLanguageTag(lang))
} else {
char.toString()
}
}
}
.joinToString { it.trim() }
status = seriesDetails.selectFirst(seriesStatusSelector)?.text().parseStatus()
thumbnail_url = seriesDetails.select(seriesThumbnailSelector).imgAttr()
}
}
override fun chapterListSelector() = "div.komik_info-chapters li"
override fun chapterFromElement(element: Element) = SChapter.create().apply {
val urlElements = element.select("a")
setUrlWithoutDomain(urlElements.attr("href"))
name = element.select(".chapter-link-item").text()
date_upload = parseChapterDate2(element.select(".chapter-link-time").text())
}
private fun parseChapterDate2(date: String): Long {
return if (date.endsWith("ago")) {
val value = date.split(' ')[0].toInt()
when {
"min" in date -> Calendar.getInstance().apply {
add(Calendar.MINUTE, value * -1)
}.timeInMillis
"hour" in date -> Calendar.getInstance().apply {
add(Calendar.HOUR_OF_DAY, value * -1)
}.timeInMillis
"day" in date -> Calendar.getInstance().apply {
add(Calendar.DATE, value * -1)
}.timeInMillis
"week" in date -> Calendar.getInstance().apply {
add(Calendar.DATE, value * 7 * -1)
}.timeInMillis
"month" in date -> Calendar.getInstance().apply {
add(Calendar.MONTH, value * -1)
}.timeInMillis
"year" in date -> Calendar.getInstance().apply {
add(Calendar.YEAR, value * -1)
}.timeInMillis
else -> {
0L
}
}
} else {
try {
dateFormat.parse(date)?.time ?: 0
} catch (_: Exception) {
0L
}
}
}
override fun pageListParse(document: Document): List<Page> {
var doc = document
var cssQuery = "div#chapter_body .main-reading-area img"
val imageListRegex = Regex("chapterImages = (.*) \\|\\|")
val imageListMatchResult = imageListRegex.find(document.toString())
if (imageListMatchResult != null) {
val imageListJson = imageListMatchResult.destructured.toList()[0]
val imageList = json.parseToJsonElement(imageListJson).jsonObject
var imageServer = "cdn"
if (!imageList.containsKey(imageServer)) imageServer = imageList.keys.first()
val imageElement = imageList[imageServer]!!.jsonArray.joinToString("")
2023-02-12 03:22:32 +00:00
doc = Jsoup.parse(imageElement)
cssQuery = "img.size-full"
}
return doc.select(cssQuery)
.mapIndexed { i, img -> Page(i, document.location(), img.imgAttr()) }
}
override val hasProjectPage: Boolean = true
override val projectPageString = "/project-list"
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
val url = baseUrl.toHttpUrl().newBuilder()
if (query.isNotEmpty()) {
url.addPathSegments("page/$page/").addQueryParameter("s", query)
} else {
url.addPathSegment(mangaUrlDirectory.substring(1)).addPathSegments("page/$page/")
}
filters.forEach { filter ->
when (filter) {
is StatusFilter -> {
url.addQueryParameter("status", filter.selectedValue())
}
is TypeFilter -> {
url.addQueryParameter("type", filter.selectedValue())
}
is OrderByFilter -> {
url.addQueryParameter("orderby", filter.selectedValue())
}
is GenreListFilter -> {
filter.state
.filter { it.state != Filter.TriState.STATE_IGNORE }
.forEach {
val value = if (it.state == Filter.TriState.STATE_EXCLUDE) "-${it.value}" else it.value
url.addQueryParameter("genre[]", value)
}
}
// if site has project page, default value "hasProjectPage" = false
is ProjectFilter -> {
if (filter.selectedValue() == "project-filter-on") {
url.setPathSegment(0, projectPageString.substring(1))
}
}
else -> { /* Do Nothing */ }
}
}
return GET(url.build())
}
private class StatusFilter : SelectFilter(
"Status",
arrayOf(
Pair("All", ""),
Pair("Ongoing", "ongoing"),
2023-02-11 19:21:03 +00:00
Pair("Completed", "completed"),
),
)
private class TypeFilter : SelectFilter(
"Type",
arrayOf(
Pair("All", ""),
Pair("Manga", "manga"),
Pair("Manhwa", "manhwa"),
2023-02-11 19:21:03 +00:00
Pair("Manhua", "manhua"),
),
)
private class OrderByFilter(defaultOrder: String? = null) : SelectFilter(
"Sort By",
arrayOf(
Pair("Default", ""),
Pair("A-Z", "titleasc"),
Pair("Z-A", "titledesc"),
Pair("Update", "update"),
2023-02-11 19:21:03 +00:00
Pair("Popular", "popular"),
),
2023-02-11 19:21:03 +00:00
defaultOrder,
)
override fun getFilterList(): FilterList {
val filters = mutableListOf<Filter<*>>(
Filter.Separator(),
StatusFilter(),
TypeFilter(),
OrderByFilter(),
Filter.Header("Genre exclusion is not available for all sources"),
GenreListFilter(getGenreList()),
Filter.Separator(),
Filter.Header("NOTE: Can't be used with other filter!"),
Filter.Header("$name Project List page"),
ProjectFilter(),
)
return FilterList(filters)
}
}