Mangathemesia refactor (#1637)
* remove randomua * i18n * add other language based selectors * countviews in background * small cleanup * lint * fix * bump * fix genre resetting * use enqueue instead of coroutinescope * fix build * fix build x2 * add back genre missing warning * Add ES translations * lint * Add available language * lint I hate lint * review - lowercase match for status - callback on site * review x2, also fix smol mistake * lint :) * lowercase some translations Co-authored-by: stevenyomi <95685115+stevenyomi@users.noreply.github.com> * lowercase some translations Co-authored-by: stevenyomi <95685115+stevenyomi@users.noreply.github.com> * remove "人気" * inline the labels * lint thank you lint, very cool --------- Co-authored-by: bapeey <90949336+bapeey@users.noreply.github.com> Co-authored-by: stevenyomi <95685115+stevenyomi@users.noreply.github.com>
This commit is contained in:
parent
8f18229563
commit
88dba59eef
|
@ -0,0 +1,30 @@
|
||||||
|
alt_names_heading=Alternative Names:
|
||||||
|
author_filter_title=Author
|
||||||
|
year_filter_title=Year
|
||||||
|
status_filter_title=Status
|
||||||
|
status_filter_option_all=All
|
||||||
|
status_filter_option_ongoing=Ongoing
|
||||||
|
status_filter_option_completed=Completed
|
||||||
|
status_filter_option_hiatus=Hiatus
|
||||||
|
status_filter_option_dropped=Dropped
|
||||||
|
type_filter_title=Type
|
||||||
|
type_filter_option_all=All
|
||||||
|
type_filter_option_manga=Manga
|
||||||
|
type_filter_option_manhwa=Manhwa
|
||||||
|
type_filter_option_manhua=Manhua
|
||||||
|
type_filter_option_comic=Comic
|
||||||
|
order_by_filter_title=Sort By
|
||||||
|
order_by_filter_default=Default
|
||||||
|
order_by_filter_az=A-Z
|
||||||
|
order_by_filter_za=Z-A
|
||||||
|
order_by_filter_latest_update=Latest Update
|
||||||
|
order_by_filter_latest_added=Latest Added
|
||||||
|
order_by_filter_popular=Popular
|
||||||
|
project_filter_title=Filter Project
|
||||||
|
project_filter_all_manga=Show all manga
|
||||||
|
project_filter_only_project=Show only project manga
|
||||||
|
genre_filter_title=Genre
|
||||||
|
genre_missing_warning=Press 'Reset' to attempt to show the genres
|
||||||
|
genre_exclusion_warning=Genre exclusion is not available for all sources
|
||||||
|
project_filter_warning=NOTE: Can't be used with other filter!
|
||||||
|
project_filter_name=%s Project List page
|
|
@ -0,0 +1,23 @@
|
||||||
|
alt_names_heading=Nombres alternativos:
|
||||||
|
author_filter_title=Autor
|
||||||
|
year_filter_title=Año
|
||||||
|
status_filter_title=Estado
|
||||||
|
status_filter_option_all=Todos
|
||||||
|
status_filter_option_ongoing=En curso
|
||||||
|
status_filter_option_completed=Completado
|
||||||
|
status_filter_option_hiatus=En pausa
|
||||||
|
status_filter_option_dropped=Abandonado
|
||||||
|
type_filter_title=Tipo
|
||||||
|
type_filter_option_all=Todos
|
||||||
|
order_by_filter_title=Ordenar por
|
||||||
|
order_by_filter_default=Por defecto
|
||||||
|
order_by_filter_latest_update=Última actualización
|
||||||
|
order_by_filter_latest_added=Último añadido
|
||||||
|
project_filter_title=Filtrar proyectos
|
||||||
|
project_filter_all_manga=Mostrar todos los mangas
|
||||||
|
project_filter_only_project=Mostrar solo los proyectos
|
||||||
|
genre_filter_title=Género
|
||||||
|
genre_missing_warning=Presione 'Restablecer' para intentar cargar los géneros
|
||||||
|
genre_exclusion_warning=La exclusión de géneros puede no funcionar correctamente
|
||||||
|
project_filter_warning=NOTA: ¡No se puede usar con otros filtros!
|
||||||
|
project_filter_name=%s Página de proyectos
|
|
@ -2,8 +2,8 @@ plugins {
|
||||||
id("lib-multisrc")
|
id("lib-multisrc")
|
||||||
}
|
}
|
||||||
|
|
||||||
baseVersionCode = 28
|
baseVersionCode = 29
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
api(project(":lib:randomua"))
|
api(project(":lib:i18n"))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,8 @@
|
||||||
package eu.kanade.tachiyomi.multisrc.mangathemesia
|
package eu.kanade.tachiyomi.multisrc.mangathemesia
|
||||||
|
|
||||||
import android.app.Application
|
import eu.kanade.tachiyomi.lib.i18n.Intl
|
||||||
import android.content.SharedPreferences
|
|
||||||
import androidx.preference.PreferenceScreen
|
|
||||||
import eu.kanade.tachiyomi.lib.randomua.addRandomUAPreferenceToScreen
|
|
||||||
import eu.kanade.tachiyomi.lib.randomua.getPrefCustomUA
|
|
||||||
import eu.kanade.tachiyomi.lib.randomua.getPrefUAType
|
|
||||||
import eu.kanade.tachiyomi.lib.randomua.setRandomUserAgent
|
|
||||||
import eu.kanade.tachiyomi.network.GET
|
import eu.kanade.tachiyomi.network.GET
|
||||||
import eu.kanade.tachiyomi.network.POST
|
import eu.kanade.tachiyomi.network.POST
|
||||||
import eu.kanade.tachiyomi.source.ConfigurableSource
|
|
||||||
import eu.kanade.tachiyomi.source.model.Filter
|
import eu.kanade.tachiyomi.source.model.Filter
|
||||||
import eu.kanade.tachiyomi.source.model.FilterList
|
import eu.kanade.tachiyomi.source.model.FilterList
|
||||||
import eu.kanade.tachiyomi.source.model.MangasPage
|
import eu.kanade.tachiyomi.source.model.MangasPage
|
||||||
|
@ -21,64 +14,56 @@ import eu.kanade.tachiyomi.util.asJsoup
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import kotlinx.serialization.json.jsonArray
|
import kotlinx.serialization.json.jsonArray
|
||||||
import kotlinx.serialization.json.jsonPrimitive
|
import kotlinx.serialization.json.jsonPrimitive
|
||||||
|
import okhttp3.Call
|
||||||
|
import okhttp3.Callback
|
||||||
import okhttp3.FormBody
|
import okhttp3.FormBody
|
||||||
import okhttp3.HttpUrl
|
import okhttp3.HttpUrl
|
||||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||||
import okhttp3.OkHttpClient
|
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
import org.jsoup.nodes.Document
|
import org.jsoup.nodes.Document
|
||||||
import org.jsoup.nodes.Element
|
import org.jsoup.nodes.Element
|
||||||
import org.jsoup.select.Elements
|
import org.jsoup.select.Elements
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
import uy.kohesive.injekt.Injekt
|
|
||||||
import uy.kohesive.injekt.api.get
|
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
import java.lang.IllegalArgumentException
|
import java.io.IOException
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import java.util.concurrent.TimeUnit
|
|
||||||
|
|
||||||
// Formerly WPMangaStream & WPMangaReader -> MangaThemesia
|
// Formerly WPMangaStream & WPMangaReader -> MangaThemesia
|
||||||
abstract class MangaThemesia(
|
abstract class MangaThemesia(
|
||||||
override val name: String,
|
override val name: String,
|
||||||
override val baseUrl: String,
|
override val baseUrl: String,
|
||||||
override val lang: String,
|
final override val lang: String,
|
||||||
val mangaUrlDirectory: String = "/manga",
|
val mangaUrlDirectory: String = "/manga",
|
||||||
val dateFormat: SimpleDateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale.US),
|
val dateFormat: SimpleDateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale.US),
|
||||||
) : ParsedHttpSource(), ConfigurableSource {
|
) : ParsedHttpSource() {
|
||||||
|
|
||||||
private val preferences: SharedPreferences by lazy {
|
|
||||||
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
|
|
||||||
}
|
|
||||||
|
|
||||||
protected open val json: Json by injectLazy()
|
protected open val json: Json by injectLazy()
|
||||||
|
|
||||||
override val supportsLatest = true
|
override val supportsLatest = true
|
||||||
|
|
||||||
override val client: OkHttpClient by lazy {
|
override val client = network.cloudflareClient
|
||||||
network.cloudflareClient.newBuilder()
|
|
||||||
.setRandomUserAgent(
|
|
||||||
preferences.getPrefUAType(),
|
|
||||||
preferences.getPrefCustomUA(),
|
|
||||||
)
|
|
||||||
.connectTimeout(10, TimeUnit.SECONDS)
|
|
||||||
.readTimeout(30, TimeUnit.SECONDS)
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun headersBuilder() = super.headersBuilder()
|
override fun headersBuilder() = super.headersBuilder()
|
||||||
.set("Referer", "$baseUrl/")
|
.set("Referer", "$baseUrl/")
|
||||||
|
|
||||||
|
protected val intl = Intl(
|
||||||
|
language = lang,
|
||||||
|
baseLanguage = "en",
|
||||||
|
availableLanguages = setOf("en", "es"),
|
||||||
|
classLoader = javaClass.classLoader!!,
|
||||||
|
)
|
||||||
|
|
||||||
open val projectPageString = "/project"
|
open val projectPageString = "/project"
|
||||||
|
|
||||||
// Popular (Search with popular order and nothing else)
|
// Popular (Search with popular order and nothing else)
|
||||||
override fun popularMangaRequest(page: Int) = searchMangaRequest(page, "", FilterList(OrderByFilter("popular")))
|
override fun popularMangaRequest(page: Int) = searchMangaRequest(page, "", popularFilter)
|
||||||
override fun popularMangaParse(response: Response) = searchMangaParse(response)
|
override fun popularMangaParse(response: Response) = searchMangaParse(response)
|
||||||
|
|
||||||
// Latest (Search with update order and nothing else)
|
// Latest (Search with update order and nothing else)
|
||||||
override fun latestUpdatesRequest(page: Int) = searchMangaRequest(page, "", FilterList(OrderByFilter("update")))
|
override fun latestUpdatesRequest(page: Int) = searchMangaRequest(page, "", latestFilter)
|
||||||
override fun latestUpdatesParse(response: Response) = searchMangaParse(response)
|
override fun latestUpdatesParse(response: Response) = searchMangaParse(response)
|
||||||
|
|
||||||
// Search
|
// Search
|
||||||
|
@ -166,22 +151,96 @@ abstract class MangaThemesia(
|
||||||
override fun searchMangaNextPageSelector() = "div.pagination .next, div.hpage .r"
|
override fun searchMangaNextPageSelector() = "div.pagination .next, div.hpage .r"
|
||||||
|
|
||||||
// Manga details
|
// Manga details
|
||||||
|
private fun selector(selector: String, contains: List<String>): String {
|
||||||
|
return contains.joinToString(", ") { selector.replace("%s", it) }
|
||||||
|
}
|
||||||
|
|
||||||
open val seriesDetailsSelector = "div.bigcontent, div.animefull, div.main-info, div.postbody"
|
open val seriesDetailsSelector = "div.bigcontent, div.animefull, div.main-info, div.postbody"
|
||||||
open val seriesTitleSelector = "h1.entry-title"
|
|
||||||
open val seriesArtistSelector = ".infotable tr:contains(artist) td:last-child, .tsinfo .imptdt:contains(artist) i, .fmed b:contains(artist)+span, span:contains(artist)"
|
open val seriesTitleSelector = "h1.entry-title, .ts-breadcrumb li:last-child span"
|
||||||
open val seriesAuthorSelector = ".infotable tr:contains(author) td:last-child, .tsinfo .imptdt:contains(author) i, .fmed b:contains(author)+span, span:contains(author)"
|
|
||||||
|
open val seriesArtistSelector = selector(
|
||||||
|
".infotable tr:contains(%s) td:last-child, .tsinfo .imptdt:contains(%s) i, .fmed b:contains(%s)+span, span:contains(%s)",
|
||||||
|
listOf(
|
||||||
|
"artist",
|
||||||
|
"Artiste",
|
||||||
|
"Artista",
|
||||||
|
"الرسام",
|
||||||
|
"الناشر",
|
||||||
|
"İllüstratör",
|
||||||
|
"Çizer",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
open val seriesAuthorSelector = selector(
|
||||||
|
".infotable tr:contains(%s) td:last-child, .tsinfo .imptdt:contains(%s) i, .fmed b:contains(%s)+span, span:contains(%s)",
|
||||||
|
listOf(
|
||||||
|
"Author",
|
||||||
|
"Auteur",
|
||||||
|
"autor",
|
||||||
|
"المؤلف",
|
||||||
|
"Mangaka",
|
||||||
|
"seniman",
|
||||||
|
"Pengarang",
|
||||||
|
"Yazar",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
open val seriesDescriptionSelector = ".desc, .entry-content[itemprop=description]"
|
open val seriesDescriptionSelector = ".desc, .entry-content[itemprop=description]"
|
||||||
open val seriesAltNameSelector = ".alternative, .wd-full:contains(alt) span, .alter, .seriestualt"
|
|
||||||
open val seriesGenreSelector = "div.gnr a, .mgen a, .seriestugenre a, span:contains(genre)"
|
open val seriesAltNameSelector = ".alternative, .wd-full:contains(alt) span, .alter, .seriestualt, " +
|
||||||
open val seriesTypeSelector = ".infotable tr:contains(type) td:last-child, .tsinfo .imptdt:contains(type) i, .tsinfo .imptdt:contains(type) a, .fmed b:contains(type)+span, span:contains(type) a, a[href*=type\\=]"
|
selector(
|
||||||
open val seriesStatusSelector = ".infotable tr:contains(status) td:last-child, .tsinfo .imptdt:contains(status) i, .fmed b:contains(status)+span span:contains(status)"
|
".infotable tr:contains(%s) td:last-child",
|
||||||
|
listOf(
|
||||||
|
"Alternative",
|
||||||
|
"Alternatif",
|
||||||
|
"الأسماء الثانوية",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
open val seriesGenreSelector = "div.gnr a, .mgen a, .seriestugenre a, " +
|
||||||
|
selector(
|
||||||
|
"span:contains(%s)",
|
||||||
|
listOf(
|
||||||
|
"genre",
|
||||||
|
"التصنيف",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
open val seriesTypeSelector = selector(
|
||||||
|
".infotable tr:contains(%s) td:last-child, .tsinfo .imptdt:contains(%s) i, .tsinfo .imptdt:contains(%s) a, .fmed b:contains(%s)+span, span:contains(%s) a",
|
||||||
|
listOf(
|
||||||
|
"type",
|
||||||
|
"ประเภท",
|
||||||
|
"النوع",
|
||||||
|
"tipe",
|
||||||
|
"Türü",
|
||||||
|
),
|
||||||
|
) + ", a[href*=type\\=]"
|
||||||
|
|
||||||
|
open val seriesStatusSelector = selector(
|
||||||
|
".infotable tr:contains(%s) td:last-child, .tsinfo .imptdt:contains(%s) i, .fmed b:contains(%s)+span span:contains(%s)",
|
||||||
|
listOf(
|
||||||
|
"status",
|
||||||
|
"Statut",
|
||||||
|
"Durum",
|
||||||
|
"連載状況",
|
||||||
|
"Estado",
|
||||||
|
"الحالة",
|
||||||
|
"حالة العمل",
|
||||||
|
"สถานะ",
|
||||||
|
"stato",
|
||||||
|
"Statüsü",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
open val seriesThumbnailSelector = ".infomanga > div[itemprop=image] img, .thumb img"
|
open val seriesThumbnailSelector = ".infomanga > div[itemprop=image] img, .thumb img"
|
||||||
|
|
||||||
open val altNamePrefix = "Alternative Name: "
|
open val altNamePrefix = "${intl["alt_names_heading"]} "
|
||||||
|
|
||||||
override fun mangaDetailsParse(document: Document) = SManga.create().apply {
|
override fun mangaDetailsParse(document: Document) = SManga.create().apply {
|
||||||
document.selectFirst(seriesDetailsSelector)?.let { seriesDetails ->
|
document.selectFirst(seriesDetailsSelector)?.let { seriesDetails ->
|
||||||
title = seriesDetails.selectFirst(seriesTitleSelector)?.text().orEmpty()
|
title = seriesDetails.selectFirst(seriesTitleSelector)!!.text()
|
||||||
artist = seriesDetails.selectFirst(seriesArtistSelector)?.ownText().removeEmptyPlaceholder()
|
artist = seriesDetails.selectFirst(seriesArtistSelector)?.ownText().removeEmptyPlaceholder()
|
||||||
author = seriesDetails.selectFirst(seriesAuthorSelector)?.ownText().removeEmptyPlaceholder()
|
author = seriesDetails.selectFirst(seriesAuthorSelector)?.ownText().removeEmptyPlaceholder()
|
||||||
description = seriesDetails.select(seriesDescriptionSelector).joinToString("\n") { it.text() }.trim()
|
description = seriesDetails.select(seriesDescriptionSelector).joinToString("\n") { it.text() }.trim()
|
||||||
|
@ -210,23 +269,42 @@ abstract class MangaThemesia(
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun String?.removeEmptyPlaceholder(): String? {
|
protected fun String?.removeEmptyPlaceholder(): String? {
|
||||||
return if (this.isNullOrBlank() || this == "-" || this == "N/A") null else this
|
return if (this.isNullOrBlank() || this == "-" || this == "N/A" || this == "n/a") null else this
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun String?.parseStatus(): Int = when {
|
open fun String?.parseStatus(): Int {
|
||||||
this == null -> SManga.UNKNOWN
|
if (this == null) return SManga.UNKNOWN
|
||||||
listOf("ongoing", "publishing").any { this.contains(it, ignoreCase = true) } -> SManga.ONGOING
|
|
||||||
this.contains("hiatus", ignoreCase = true) -> SManga.ON_HIATUS
|
return when (this.lowercase().trim()) {
|
||||||
this.contains("completed", ignoreCase = true) -> SManga.COMPLETED
|
"مستمرة", "en curso", "ongoing", "on going", "ativo", "en cours",
|
||||||
listOf("dropped", "cancelled").any { this.contains(it, ignoreCase = true) } -> SManga.CANCELLED
|
"en cours de publication", "đang tiến hành", "em lançamento", "онгоінг", "publishing",
|
||||||
|
"devam ediyor", "em andamento", "in corso", "güncel", "berjalan", "продолжается", "updating", "lançando", "in arrivo", "emision",
|
||||||
|
"en emision", "مستمر", "curso", "en marcha", "publicandose", "publicando", "连载中", "devam etmekte", "連載中",
|
||||||
|
-> SManga.ONGOING
|
||||||
|
|
||||||
|
"completed", "completo", "complété", "fini", "achevé", "terminé", "tamamlandı", "đã hoàn thành", "hoàn thành",
|
||||||
|
"مكتملة", "завершено", "finished", "finalizado", "completata", "one-shot", "bitti", "tamat", "completado", "concluído", "完結",
|
||||||
|
"concluido", "已完结", "bitmiş",
|
||||||
|
-> SManga.COMPLETED
|
||||||
|
|
||||||
|
"canceled", "cancelled", "cancelado", "cancellato", "cancelados", "dropped", "discontinued", "abandonné",
|
||||||
|
-> SManga.CANCELLED
|
||||||
|
|
||||||
|
"hiatus", "on hold", "pausado", "en espera", "en pause", "en attente",
|
||||||
|
-> SManga.ON_HIATUS
|
||||||
|
|
||||||
else -> SManga.UNKNOWN
|
else -> SManga.UNKNOWN
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Chapter list
|
// Chapter list
|
||||||
override fun chapterListSelector() = "div.bxcl li, div.cl li, #chapterlist li, ul li:has(div.chbox):has(div.eph-num)"
|
override fun chapterListSelector() = "div.bxcl li, div.cl li, #chapterlist li, ul li:has(div.chbox):has(div.eph-num)"
|
||||||
|
|
||||||
override fun chapterListParse(response: Response): List<SChapter> {
|
override fun chapterListParse(response: Response): List<SChapter> {
|
||||||
val document = response.asJsoup()
|
val document = response.asJsoup()
|
||||||
|
|
||||||
|
countViews(document)
|
||||||
|
|
||||||
val chapters = document.select(chapterListSelector()).map { chapterFromElement(it) }
|
val chapters = document.select(chapterListSelector()).map { chapterFromElement(it) }
|
||||||
|
|
||||||
// Add timestamp to latest chapter, taken from "Updated On".
|
// Add timestamp to latest chapter, taken from "Updated On".
|
||||||
|
@ -238,8 +316,6 @@ abstract class MangaThemesia(
|
||||||
if (date.isNotEmpty()) chapters.first().date_upload = parseUpdatedOnDate(date)
|
if (date.isNotEmpty()) chapters.first().date_upload = parseUpdatedOnDate(date)
|
||||||
}
|
}
|
||||||
|
|
||||||
countViews(document)
|
|
||||||
|
|
||||||
return chapters
|
return chapters
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -267,13 +343,13 @@ abstract class MangaThemesia(
|
||||||
open val pageSelector = "div#readerarea img"
|
open val pageSelector = "div#readerarea img"
|
||||||
|
|
||||||
override fun pageListParse(document: Document): List<Page> {
|
override fun pageListParse(document: Document): List<Page> {
|
||||||
|
countViews(document)
|
||||||
|
|
||||||
val chapterUrl = document.location()
|
val chapterUrl = document.location()
|
||||||
val htmlPages = document.select(pageSelector)
|
val htmlPages = document.select(pageSelector)
|
||||||
.filterNot { it.imgAttr().isEmpty() }
|
.filterNot { it.imgAttr().isEmpty() }
|
||||||
.mapIndexed { i, img -> Page(i, chapterUrl, img.imgAttr()) }
|
.mapIndexed { i, img -> Page(i, chapterUrl, img.imgAttr()) }
|
||||||
|
|
||||||
countViews(document)
|
|
||||||
|
|
||||||
// Some sites also loads pages via javascript
|
// Some sites also loads pages via javascript
|
||||||
if (htmlPages.isNotEmpty()) { return htmlPages }
|
if (htmlPages.isNotEmpty()) { return htmlPages }
|
||||||
|
|
||||||
|
@ -320,8 +396,6 @@ abstract class MangaThemesia(
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
val newHeaders = headersBuilder()
|
val newHeaders = headersBuilder()
|
||||||
.set("Content-Length", formBody.contentLength().toString())
|
|
||||||
.set("Content-Type", formBody.contentType().toString())
|
|
||||||
.set("Referer", document.location())
|
.set("Referer", document.location())
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
|
@ -339,17 +413,22 @@ abstract class MangaThemesia(
|
||||||
}
|
}
|
||||||
|
|
||||||
val request = countViewsRequest(document) ?: return
|
val request = countViewsRequest(document) ?: return
|
||||||
runCatching { client.newCall(request).execute().close() }
|
val callback = object : Callback {
|
||||||
|
override fun onResponse(call: Call, response: Response) = response.close()
|
||||||
|
override fun onFailure(call: Call, e: IOException) = Unit
|
||||||
|
}
|
||||||
|
|
||||||
|
client.newCall(request).enqueue(callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filters
|
// Filters
|
||||||
protected class AuthorFilter : Filter.Text("Author")
|
protected class AuthorFilter(name: String) : Filter.Text(name)
|
||||||
|
|
||||||
protected class YearFilter : Filter.Text("Year")
|
protected class YearFilter(name: String) : Filter.Text(name)
|
||||||
|
|
||||||
open class SelectFilter(
|
open class SelectFilter(
|
||||||
displayName: String,
|
displayName: String,
|
||||||
val vals: Array<Pair<String, String>>,
|
private val vals: Array<Pair<String, String>>,
|
||||||
defaultValue: String? = null,
|
defaultValue: String? = null,
|
||||||
) : Filter.Select<String>(
|
) : Filter.Select<String>(
|
||||||
displayName,
|
displayName,
|
||||||
|
@ -359,63 +438,91 @@ abstract class MangaThemesia(
|
||||||
fun selectedValue() = vals[state].second
|
fun selectedValue() = vals[state].second
|
||||||
}
|
}
|
||||||
|
|
||||||
protected class StatusFilter : SelectFilter(
|
protected class StatusFilter(
|
||||||
"Status",
|
name: String,
|
||||||
arrayOf(
|
options: Array<Pair<String, String>>,
|
||||||
Pair("All", ""),
|
) : SelectFilter(
|
||||||
Pair("Ongoing", "ongoing"),
|
name,
|
||||||
Pair("Completed", "completed"),
|
options,
|
||||||
Pair("Hiatus", "hiatus"),
|
|
||||||
Pair("Dropped", "dropped"),
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
protected class TypeFilter : SelectFilter(
|
protected open val statusOptions = arrayOf(
|
||||||
"Type",
|
Pair(intl["status_filter_option_all"], ""),
|
||||||
arrayOf(
|
Pair(intl["status_filter_option_ongoing"], "ongoing"),
|
||||||
Pair("All", ""),
|
Pair(intl["status_filter_option_completed"], "completed"),
|
||||||
Pair("Manga", "Manga"),
|
Pair(intl["status_filter_option_hiatus"], "hiatus"),
|
||||||
Pair("Manhwa", "Manhwa"),
|
Pair(intl["status_filter_option_dropped"], "dropped"),
|
||||||
Pair("Manhua", "Manhua"),
|
|
||||||
Pair("Comic", "Comic"),
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
protected class OrderByFilter(defaultOrder: String? = null) : SelectFilter(
|
protected class TypeFilter(
|
||||||
"Sort By",
|
name: String,
|
||||||
arrayOf(
|
options: Array<Pair<String, String>>,
|
||||||
Pair("Default", ""),
|
) : SelectFilter(
|
||||||
Pair("A-Z", "title"),
|
name,
|
||||||
Pair("Z-A", "titlereverse"),
|
options,
|
||||||
Pair("Latest Update", "update"),
|
)
|
||||||
Pair("Latest Added", "latest"),
|
|
||||||
Pair("Popular", "popular"),
|
protected open val typeFilterOptions = arrayOf(
|
||||||
),
|
Pair(intl["type_filter_option_all"], ""),
|
||||||
|
Pair(intl["type_filter_option_manga"], "Manga"),
|
||||||
|
Pair(intl["type_filter_option_manhwa"], "Manhwa"),
|
||||||
|
Pair(intl["type_filter_option_manhua"], "Manhua"),
|
||||||
|
Pair(intl["type_filter_option_comic"], "Comic"),
|
||||||
|
)
|
||||||
|
|
||||||
|
protected class OrderByFilter(
|
||||||
|
name: String,
|
||||||
|
options: Array<Pair<String, String>>,
|
||||||
|
defaultOrder: String? = null,
|
||||||
|
) : SelectFilter(
|
||||||
|
name,
|
||||||
|
options,
|
||||||
defaultOrder,
|
defaultOrder,
|
||||||
)
|
)
|
||||||
|
|
||||||
protected class ProjectFilter : SelectFilter(
|
protected open val orderByFilterOptions = arrayOf(
|
||||||
"Filter Project",
|
Pair(intl["order_by_filter_default"], ""),
|
||||||
arrayOf(
|
Pair(intl["order_by_filter_az"], "title"),
|
||||||
Pair("Show all manga", ""),
|
Pair(intl["order_by_filter_za"], "titlereverse"),
|
||||||
Pair("Show only project manga", "project-filter-on"),
|
Pair(intl["order_by_filter_latest_update"], "update"),
|
||||||
),
|
Pair(intl["order_by_filter_latest_added"], "latest"),
|
||||||
|
Pair(intl["order_by_filter_popular"], "popular"),
|
||||||
|
)
|
||||||
|
|
||||||
|
protected val popularFilter by lazy { FilterList(OrderByFilter("", orderByFilterOptions, "popular")) }
|
||||||
|
protected val latestFilter by lazy { FilterList(OrderByFilter("", orderByFilterOptions, "update")) }
|
||||||
|
|
||||||
|
protected class ProjectFilter(
|
||||||
|
name: String,
|
||||||
|
options: Array<Pair<String, String>>,
|
||||||
|
) : SelectFilter(
|
||||||
|
name,
|
||||||
|
options,
|
||||||
|
)
|
||||||
|
|
||||||
|
protected open val projectFilterOptions = arrayOf(
|
||||||
|
Pair(intl["project_filter_all_manga"], ""),
|
||||||
|
Pair(intl["project_filter_only_project"], "project-filter-on"),
|
||||||
|
)
|
||||||
|
|
||||||
|
protected class GenreData(
|
||||||
|
val name: String,
|
||||||
|
val value: String,
|
||||||
|
val state: Int = Filter.TriState.STATE_IGNORE,
|
||||||
)
|
)
|
||||||
|
|
||||||
protected class Genre(
|
protected class Genre(
|
||||||
name: String,
|
name: String,
|
||||||
val value: String,
|
val value: String,
|
||||||
state: Int = STATE_IGNORE,
|
state: Int,
|
||||||
) : Filter.TriState(name, state)
|
) : Filter.TriState(name, state)
|
||||||
|
|
||||||
protected class GenreListFilter(genres: List<Genre>) : Filter.Group<Genre>("Genre", genres)
|
protected class GenreListFilter(name: String, genres: List<Genre>) : Filter.Group<Genre>(name, genres)
|
||||||
|
|
||||||
|
protected var genrelist: List<GenreData>? = null
|
||||||
|
|
||||||
private var genrelist: List<Genre>? = null
|
|
||||||
protected open fun getGenreList(): List<Genre> {
|
protected open fun getGenreList(): List<Genre> {
|
||||||
// Filters are fetched immediately once an extension loads
|
return genrelist?.map { Genre(it.name, it.value, it.state) }.orEmpty()
|
||||||
// We're only able to get filters after a loading the manga directory,
|
|
||||||
// and resetting the filters is the only thing that seems to reinflate the view
|
|
||||||
return genrelist ?: listOf(Genre("Press reset to attempt to fetch genres", ""))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
open val hasProjectPage = false
|
open val hasProjectPage = false
|
||||||
|
@ -423,21 +530,31 @@ abstract class MangaThemesia(
|
||||||
override fun getFilterList(): FilterList {
|
override fun getFilterList(): FilterList {
|
||||||
val filters = mutableListOf<Filter<*>>(
|
val filters = mutableListOf<Filter<*>>(
|
||||||
Filter.Separator(),
|
Filter.Separator(),
|
||||||
AuthorFilter(),
|
AuthorFilter(intl["author_filter_title"]),
|
||||||
YearFilter(),
|
YearFilter(intl["year_filter_title"]),
|
||||||
StatusFilter(),
|
StatusFilter(intl["status_filter_title"], statusOptions),
|
||||||
TypeFilter(),
|
TypeFilter(intl["type_filter_title"], typeFilterOptions),
|
||||||
OrderByFilter(),
|
OrderByFilter(intl["order_by_filter_title"], orderByFilterOptions),
|
||||||
Filter.Header("Genre exclusion is not available for all sources"),
|
|
||||||
GenreListFilter(getGenreList()),
|
|
||||||
)
|
)
|
||||||
|
if (!genrelist.isNullOrEmpty()) {
|
||||||
|
filters.addAll(
|
||||||
|
listOf(
|
||||||
|
Filter.Header(intl["genre_exclusion_warning"]),
|
||||||
|
GenreListFilter(intl["genre_filter_title"], getGenreList()),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
filters.add(
|
||||||
|
Filter.Header(intl["genre_missing_warning"]),
|
||||||
|
)
|
||||||
|
}
|
||||||
if (hasProjectPage) {
|
if (hasProjectPage) {
|
||||||
filters.addAll(
|
filters.addAll(
|
||||||
mutableListOf<Filter<*>>(
|
mutableListOf<Filter<*>>(
|
||||||
Filter.Separator(),
|
Filter.Separator(),
|
||||||
Filter.Header("NOTE: Can't be used with other filter!"),
|
Filter.Header(intl["project_filter_warning"]),
|
||||||
Filter.Header("$name Project List page"),
|
Filter.Header(intl.format("project_filter_name", name)),
|
||||||
ProjectFilter(),
|
ProjectFilter(intl["project_filter_title"], projectFilterOptions),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -485,9 +602,9 @@ abstract class MangaThemesia(
|
||||||
(!strict && url.pathSegments.size == n + 1 && url.pathSegments[n].isEmpty())
|
(!strict && url.pathSegments.size == n + 1 && url.pathSegments[n].isEmpty())
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun parseGenres(document: Document): List<Genre>? {
|
private fun parseGenres(document: Document): List<GenreData>? {
|
||||||
return document.selectFirst("ul.genrez")?.select("li")?.map { li ->
|
return document.selectFirst("ul.genrez")?.select("li")?.map { li ->
|
||||||
Genre(
|
GenreData(
|
||||||
li.selectFirst("label")!!.text(),
|
li.selectFirst("label")!!.text(),
|
||||||
li.selectFirst("input[type=checkbox]")!!.attr("value"),
|
li.selectFirst("input[type=checkbox]")!!.attr("value"),
|
||||||
)
|
)
|
||||||
|
@ -514,15 +631,10 @@ abstract class MangaThemesia(
|
||||||
|
|
||||||
override fun imageUrlParse(document: Document): String = throw UnsupportedOperationException()
|
override fun imageUrlParse(document: Document): String = throw UnsupportedOperationException()
|
||||||
|
|
||||||
override fun setupPreferenceScreen(screen: PreferenceScreen) {
|
|
||||||
addRandomUAPreferenceToScreen(screen)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val URL_SEARCH_PREFIX = "url:"
|
const val URL_SEARCH_PREFIX = "url:"
|
||||||
|
|
||||||
// More info: https://issuetracker.google.com/issues/36970498
|
// More info: https://issuetracker.google.com/issues/36970498
|
||||||
@Suppress("RegExpRedundantEscape")
|
|
||||||
private val MANGA_PAGE_ID_REGEX = "post_id\\s*:\\s*(\\d+)\\}".toRegex()
|
private val MANGA_PAGE_ID_REGEX = "post_id\\s*:\\s*(\\d+)\\}".toRegex()
|
||||||
private val CHAPTER_PAGE_ID_REGEX = "chapter_id\\s*=\\s*(\\d+);".toRegex()
|
private val CHAPTER_PAGE_ID_REGEX = "chapter_id\\s*=\\s*(\\d+);".toRegex()
|
||||||
|
|
||||||
|
|
|
@ -33,10 +33,11 @@ open class MiauScan(lang: String) : MangaThemesia(
|
||||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
||||||
val genreFilterIndex = filters.indexOfFirst { it is GenreListFilter }
|
val genreFilterIndex = filters.indexOfFirst { it is GenreListFilter }
|
||||||
val genreFilter = filters.getOrNull(genreFilterIndex) as? GenreListFilter
|
val genreFilter = filters.getOrNull(genreFilterIndex) as? GenreListFilter
|
||||||
?: GenreListFilter(emptyList())
|
?: GenreListFilter("", emptyList())
|
||||||
|
|
||||||
val overloadedGenreFilter = GenreListFilter(
|
val overloadedGenreFilter = GenreListFilter(
|
||||||
genres = genreFilter.state + listOf(
|
genreFilter.name,
|
||||||
|
genreFilter.state + listOf(
|
||||||
Genre("", PORTUGUESE_GENRE_ID, portugueseMode),
|
Genre("", PORTUGUESE_GENRE_ID, portugueseMode),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
@ -31,8 +31,8 @@ class Mihentai : MangaThemesia("Mihentai", "https://mihentai.com", "all") {
|
||||||
listOf(
|
listOf(
|
||||||
StatusFilter(),
|
StatusFilter(),
|
||||||
TypeFilter(),
|
TypeFilter(),
|
||||||
OrderByFilter(),
|
OrderByFilter(intl["order_by_filter_title"], orderByFilterOptions),
|
||||||
GenreListFilter(getGenreList()),
|
GenreListFilter(intl["genre_filter_title"], getGenreList()),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package eu.kanade.tachiyomi.extension.ar.areamanga
|
package eu.kanade.tachiyomi.extension.ar.areamanga
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
|
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
|
||||||
import eu.kanade.tachiyomi.source.model.SManga
|
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
|
||||||
|
@ -10,21 +9,4 @@ class AreaManga : MangaThemesia(
|
||||||
"https://www.areascans.net",
|
"https://www.areascans.net",
|
||||||
"ar",
|
"ar",
|
||||||
dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale("ar")),
|
dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale("ar")),
|
||||||
) {
|
)
|
||||||
override val seriesArtistSelector =
|
|
||||||
".tsinfo .imptdt:contains(الرسام) i, ${super.seriesArtistSelector}"
|
|
||||||
override val seriesAuthorSelector =
|
|
||||||
".tsinfo .imptdt:contains(المؤلف) i, ${super.seriesAuthorSelector}"
|
|
||||||
override val seriesStatusSelector =
|
|
||||||
".tsinfo .imptdt:contains(الحالة) i, ${super.seriesStatusSelector}"
|
|
||||||
override val seriesTypeSelector =
|
|
||||||
".tsinfo .imptdt:contains(النوع) i, ${super.seriesTypeSelector}"
|
|
||||||
|
|
||||||
override fun String?.parseStatus() = when {
|
|
||||||
this == null -> SManga.UNKNOWN
|
|
||||||
this.contains("مستمر", ignoreCase = true) -> SManga.ONGOING
|
|
||||||
this.contains("مكتمل", ignoreCase = true) -> SManga.COMPLETED
|
|
||||||
this.contains("متوقف", ignoreCase = true) -> SManga.ON_HIATUS
|
|
||||||
else -> SManga.UNKNOWN
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ import androidx.preference.PreferenceScreen
|
||||||
import eu.kanade.tachiyomi.extension.BuildConfig
|
import eu.kanade.tachiyomi.extension.BuildConfig
|
||||||
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
|
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
|
||||||
import eu.kanade.tachiyomi.network.interceptor.rateLimit
|
import eu.kanade.tachiyomi.network.interceptor.rateLimit
|
||||||
|
import eu.kanade.tachiyomi.source.ConfigurableSource
|
||||||
import eu.kanade.tachiyomi.source.model.FilterList
|
import eu.kanade.tachiyomi.source.model.FilterList
|
||||||
import eu.kanade.tachiyomi.source.model.Page
|
import eu.kanade.tachiyomi.source.model.Page
|
||||||
import eu.kanade.tachiyomi.source.model.SChapter
|
import eu.kanade.tachiyomi.source.model.SChapter
|
||||||
|
@ -23,12 +24,14 @@ import java.util.Locale
|
||||||
|
|
||||||
private const val swatUrl = "https://swatmanhua.com"
|
private const val swatUrl = "https://swatmanhua.com"
|
||||||
|
|
||||||
class MangaSwat : MangaThemesia(
|
class MangaSwat :
|
||||||
|
MangaThemesia(
|
||||||
"MangaSwat",
|
"MangaSwat",
|
||||||
swatUrl,
|
swatUrl,
|
||||||
"ar",
|
"ar",
|
||||||
dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale("ar")),
|
dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale("ar")),
|
||||||
) {
|
),
|
||||||
|
ConfigurableSource {
|
||||||
private val defaultBaseUrl = swatUrl
|
private val defaultBaseUrl = swatUrl
|
||||||
|
|
||||||
override val baseUrl by lazy { getPrefBaseUrl() }
|
override val baseUrl by lazy { getPrefBaseUrl() }
|
||||||
|
@ -113,8 +116,6 @@ class MangaSwat : MangaThemesia(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
screen.addPreference(baseUrlPref)
|
screen.addPreference(baseUrlPref)
|
||||||
|
|
||||||
super.setupPreferenceScreen(screen)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getPrefBaseUrl(): String = preferences.getString(BASE_URL_PREF, defaultBaseUrl)!!
|
private fun getPrefBaseUrl(): String = preferences.getString(BASE_URL_PREF, defaultBaseUrl)!!
|
||||||
|
|
|
@ -6,6 +6,7 @@ import androidx.preference.PreferenceScreen
|
||||||
import androidx.preference.SwitchPreferenceCompat
|
import androidx.preference.SwitchPreferenceCompat
|
||||||
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
|
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
|
||||||
import eu.kanade.tachiyomi.network.interceptor.rateLimit
|
import eu.kanade.tachiyomi.network.interceptor.rateLimit
|
||||||
|
import eu.kanade.tachiyomi.source.ConfigurableSource
|
||||||
import eu.kanade.tachiyomi.source.model.FilterList
|
import eu.kanade.tachiyomi.source.model.FilterList
|
||||||
import eu.kanade.tachiyomi.source.model.MangasPage
|
import eu.kanade.tachiyomi.source.model.MangasPage
|
||||||
import eu.kanade.tachiyomi.source.model.Page
|
import eu.kanade.tachiyomi.source.model.Page
|
||||||
|
@ -26,12 +27,14 @@ import java.text.SimpleDateFormat
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
class AsuraScans : MangaThemesia(
|
class AsuraScans :
|
||||||
|
MangaThemesia(
|
||||||
"Asura Scans",
|
"Asura Scans",
|
||||||
"https://asuratoon.com",
|
"https://asuratoon.com",
|
||||||
"en",
|
"en",
|
||||||
dateFormat = SimpleDateFormat("MMM d, yyyy", Locale.US),
|
dateFormat = SimpleDateFormat("MMM d, yyyy", Locale.US),
|
||||||
) {
|
),
|
||||||
|
ConfigurableSource {
|
||||||
|
|
||||||
private val preferences by lazy {
|
private val preferences by lazy {
|
||||||
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
|
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
|
||||||
|
@ -281,8 +284,6 @@ class AsuraScans : MangaThemesia(
|
||||||
summary = PREF_PERM_MANGA_URL_SUMMARY
|
summary = PREF_PERM_MANGA_URL_SUMMARY
|
||||||
setDefaultValue(true)
|
setDefaultValue(true)
|
||||||
}.also(screen::addPreference)
|
}.also(screen::addPreference)
|
||||||
|
|
||||||
super.setupPreferenceScreen(screen)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private val SharedPreferences.permaUrlPref
|
private val SharedPreferences.permaUrlPref
|
||||||
|
|
|
@ -8,3 +8,7 @@ ext {
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$rootDir/common.gradle"
|
apply from: "$rootDir/common.gradle"
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(project(":lib:randomua"))
|
||||||
|
}
|
||||||
|
|
|
@ -2,11 +2,14 @@ package eu.kanade.tachiyomi.extension.en.constellarscans
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
|
import androidx.preference.PreferenceScreen
|
||||||
|
import eu.kanade.tachiyomi.lib.randomua.addRandomUAPreferenceToScreen
|
||||||
import eu.kanade.tachiyomi.lib.randomua.getPrefCustomUA
|
import eu.kanade.tachiyomi.lib.randomua.getPrefCustomUA
|
||||||
import eu.kanade.tachiyomi.lib.randomua.getPrefUAType
|
import eu.kanade.tachiyomi.lib.randomua.getPrefUAType
|
||||||
import eu.kanade.tachiyomi.lib.randomua.setRandomUserAgent
|
import eu.kanade.tachiyomi.lib.randomua.setRandomUserAgent
|
||||||
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
|
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
|
||||||
import eu.kanade.tachiyomi.network.interceptor.rateLimit
|
import eu.kanade.tachiyomi.network.interceptor.rateLimit
|
||||||
|
import eu.kanade.tachiyomi.source.ConfigurableSource
|
||||||
import eu.kanade.tachiyomi.source.model.Page
|
import eu.kanade.tachiyomi.source.model.Page
|
||||||
import eu.kanade.tachiyomi.source.model.SChapter
|
import eu.kanade.tachiyomi.source.model.SChapter
|
||||||
import kotlinx.serialization.json.jsonArray
|
import kotlinx.serialization.json.jsonArray
|
||||||
|
@ -19,22 +22,29 @@ import okhttp3.Request
|
||||||
import org.jsoup.nodes.Document
|
import org.jsoup.nodes.Document
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
import java.util.concurrent.TimeUnit
|
|
||||||
|
|
||||||
class ConstellarScans : MangaThemesia("Constellar Scans", "https://constellarcomic.com", "en") {
|
class ConstellarScans :
|
||||||
|
MangaThemesia(
|
||||||
|
"Constellar Scans",
|
||||||
|
"https://constellarcomic.com",
|
||||||
|
"en",
|
||||||
|
),
|
||||||
|
ConfigurableSource {
|
||||||
|
|
||||||
private val preferences: SharedPreferences by lazy {
|
private val preferences: SharedPreferences by lazy {
|
||||||
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
|
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun setupPreferenceScreen(screen: PreferenceScreen) {
|
||||||
|
addRandomUAPreferenceToScreen(screen)
|
||||||
|
}
|
||||||
|
|
||||||
override val client: OkHttpClient by lazy {
|
override val client: OkHttpClient by lazy {
|
||||||
network.cloudflareClient.newBuilder()
|
network.cloudflareClient.newBuilder()
|
||||||
.setRandomUserAgent(
|
.setRandomUserAgent(
|
||||||
preferences.getPrefUAType(),
|
preferences.getPrefUAType(),
|
||||||
preferences.getPrefCustomUA(),
|
preferences.getPrefCustomUA(),
|
||||||
)
|
)
|
||||||
.connectTimeout(10, TimeUnit.SECONDS)
|
|
||||||
.readTimeout(30, TimeUnit.SECONDS)
|
|
||||||
.rateLimit(1, 1)
|
.rateLimit(1, 1)
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
@ -61,6 +71,8 @@ class ConstellarScans : MangaThemesia("Constellar Scans", "https://constellarcom
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
override fun pageListParse(document: Document): List<Page> {
|
override fun pageListParse(document: Document): List<Page> {
|
||||||
|
countViews(document)
|
||||||
|
|
||||||
val html = document.toString()
|
val html = document.toString()
|
||||||
if (!html.contains("ts_rea_der_._run(\"")) {
|
if (!html.contains("ts_rea_der_._run(\"")) {
|
||||||
return super.pageListParse(document)
|
return super.pageListParse(document)
|
||||||
|
@ -80,7 +92,6 @@ class ConstellarScans : MangaThemesia("Constellar Scans", "https://constellarcom
|
||||||
}
|
}
|
||||||
.joinToString("")
|
.joinToString("")
|
||||||
|
|
||||||
countViews(document)
|
|
||||||
return json.parseToJsonElement(tsReaderRawData).jsonObject["sources"]!!.jsonArray[0].jsonObject["images"]!!.jsonArray.mapIndexed { idx, it ->
|
return json.parseToJsonElement(tsReaderRawData).jsonObject["sources"]!!.jsonArray[0].jsonObject["images"]!!.jsonArray.mapIndexed { idx, it ->
|
||||||
Page(idx, imageUrl = it.jsonPrimitive.content)
|
Page(idx, imageUrl = it.jsonPrimitive.content)
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import androidx.preference.PreferenceScreen
|
||||||
import androidx.preference.SwitchPreferenceCompat
|
import androidx.preference.SwitchPreferenceCompat
|
||||||
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
|
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
|
||||||
import eu.kanade.tachiyomi.network.interceptor.rateLimit
|
import eu.kanade.tachiyomi.network.interceptor.rateLimit
|
||||||
|
import eu.kanade.tachiyomi.source.ConfigurableSource
|
||||||
import eu.kanade.tachiyomi.source.model.FilterList
|
import eu.kanade.tachiyomi.source.model.FilterList
|
||||||
import eu.kanade.tachiyomi.source.model.MangasPage
|
import eu.kanade.tachiyomi.source.model.MangasPage
|
||||||
import eu.kanade.tachiyomi.source.model.Page
|
import eu.kanade.tachiyomi.source.model.Page
|
||||||
|
@ -25,12 +26,14 @@ import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
import java.io.ByteArrayOutputStream
|
import java.io.ByteArrayOutputStream
|
||||||
|
|
||||||
class FlameComics : MangaThemesia(
|
class FlameComics :
|
||||||
|
MangaThemesia(
|
||||||
"Flame Comics",
|
"Flame Comics",
|
||||||
"https://flamecomics.com",
|
"https://flamecomics.com",
|
||||||
"en",
|
"en",
|
||||||
mangaUrlDirectory = "/series",
|
mangaUrlDirectory = "/series",
|
||||||
) {
|
),
|
||||||
|
ConfigurableSource {
|
||||||
|
|
||||||
// Flame Scans -> Flame Comics
|
// Flame Scans -> Flame Comics
|
||||||
override val id = 6350607071566689772
|
override val id = 6350607071566689772
|
||||||
|
|
|
@ -6,6 +6,7 @@ import androidx.preference.PreferenceScreen
|
||||||
import androidx.preference.SwitchPreferenceCompat
|
import androidx.preference.SwitchPreferenceCompat
|
||||||
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
|
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
|
||||||
import eu.kanade.tachiyomi.network.interceptor.rateLimit
|
import eu.kanade.tachiyomi.network.interceptor.rateLimit
|
||||||
|
import eu.kanade.tachiyomi.source.ConfigurableSource
|
||||||
import eu.kanade.tachiyomi.source.model.FilterList
|
import eu.kanade.tachiyomi.source.model.FilterList
|
||||||
import eu.kanade.tachiyomi.source.model.MangasPage
|
import eu.kanade.tachiyomi.source.model.MangasPage
|
||||||
import eu.kanade.tachiyomi.source.model.SChapter
|
import eu.kanade.tachiyomi.source.model.SChapter
|
||||||
|
@ -20,7 +21,15 @@ import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
class LuminousScans : MangaThemesia("Luminous Scans", "https://lumitoon.com", "en", mangaUrlDirectory = "/series") {
|
class LuminousScans :
|
||||||
|
MangaThemesia(
|
||||||
|
"Luminous Scans",
|
||||||
|
"https://lumitoon.com",
|
||||||
|
"en",
|
||||||
|
mangaUrlDirectory = "/series",
|
||||||
|
),
|
||||||
|
ConfigurableSource {
|
||||||
|
|
||||||
override val client = super.client.newBuilder()
|
override val client = super.client.newBuilder()
|
||||||
.addInterceptor(::urlChangeInterceptor)
|
.addInterceptor(::urlChangeInterceptor)
|
||||||
.rateLimit(2)
|
.rateLimit(2)
|
||||||
|
@ -201,8 +210,6 @@ class LuminousScans : MangaThemesia("Luminous Scans", "https://lumitoon.com", "e
|
||||||
summary = PREF_PERM_MANGA_URL_SUMMARY
|
summary = PREF_PERM_MANGA_URL_SUMMARY
|
||||||
setDefaultValue(true)
|
setDefaultValue(true)
|
||||||
}.also(screen::addPreference)
|
}.also(screen::addPreference)
|
||||||
|
|
||||||
super.setupPreferenceScreen(screen)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private val SharedPreferences.permaUrlPref
|
private val SharedPreferences.permaUrlPref
|
||||||
|
|
|
@ -33,19 +33,29 @@ class LunarScans : MangaThemesia(
|
||||||
val filters = mutableListOf<Filter<*>>(
|
val filters = mutableListOf<Filter<*>>(
|
||||||
Filter.Header("Note: Can't be used with text search!"),
|
Filter.Header("Note: Can't be used with text search!"),
|
||||||
Filter.Separator(),
|
Filter.Separator(),
|
||||||
StatusFilter(),
|
StatusFilter(intl["status_filter_title"], statusOptions),
|
||||||
TypeFilter(),
|
TypeFilter(intl["type_filter_title"], typeFilterOptions),
|
||||||
OrderByFilter(),
|
OrderByFilter(intl["order_by_filter_title"], orderByFilterOptions),
|
||||||
Filter.Header("Genre exclusion is not available for all sources"),
|
|
||||||
GenreListFilter(getGenreList()),
|
|
||||||
)
|
)
|
||||||
|
if (!genrelist.isNullOrEmpty()) {
|
||||||
|
filters.addAll(
|
||||||
|
listOf(
|
||||||
|
Filter.Header(intl["genre_exclusion_warning"]),
|
||||||
|
GenreListFilter(intl["genre_filter_title"], getGenreList()),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
filters.add(
|
||||||
|
Filter.Header(intl["genre_missing_warning"]),
|
||||||
|
)
|
||||||
|
}
|
||||||
if (hasProjectPage) {
|
if (hasProjectPage) {
|
||||||
filters.addAll(
|
filters.addAll(
|
||||||
mutableListOf<Filter<*>>(
|
mutableListOf<Filter<*>>(
|
||||||
Filter.Separator(),
|
Filter.Separator(),
|
||||||
Filter.Header("NOTE: Can't be used with other filter!"),
|
Filter.Header(intl["project_filter_warning"]),
|
||||||
Filter.Header("$name Project List page"),
|
Filter.Header(intl.format("project_filter_name", name)),
|
||||||
ProjectFilter(),
|
ProjectFilter(intl["project_filter_title"], projectFilterOptions),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,14 +17,4 @@ class CarteldeManhwas : MangaThemesia(
|
||||||
override fun searchMangaSelector() = ".utao .uta .imgu:not(:has(span.novelabel)), " +
|
override fun searchMangaSelector() = ".utao .uta .imgu:not(:has(span.novelabel)), " +
|
||||||
".listupd .bs .bsx:not(:has(span.novelabel)), " +
|
".listupd .bs .bsx:not(:has(span.novelabel)), " +
|
||||||
".listo .bs .bsx:not(:has(span.novelabel))"
|
".listo .bs .bsx:not(:has(span.novelabel))"
|
||||||
|
|
||||||
private class StatusFilter : SelectFilter(
|
|
||||||
"Status",
|
|
||||||
arrayOf(
|
|
||||||
Pair("All", ""),
|
|
||||||
Pair("Ongoing", "ongoing"),
|
|
||||||
Pair("Completed", "completed"),
|
|
||||||
Pair("Hiatus", "hiatus"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,12 @@
|
||||||
package eu.kanade.tachiyomi.extension.es.gremorymangas
|
package eu.kanade.tachiyomi.extension.es.gremorymangas
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
|
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
class GremoryMangas : MangaThemesia("Gremory Mangas", "https://gremorymangas.com", "es")
|
class GremoryMangas : MangaThemesia(
|
||||||
|
"Gremory Mangas",
|
||||||
|
"https://gremorymangas.com",
|
||||||
|
"es",
|
||||||
|
dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale("es")),
|
||||||
|
)
|
||||||
|
|
|
@ -25,13 +25,13 @@ class NekoScans : MangaThemesia(
|
||||||
override val seriesStatusSelector = ".tsinfo .imptdt:contains(estado) i"
|
override val seriesStatusSelector = ".tsinfo .imptdt:contains(estado) i"
|
||||||
|
|
||||||
override fun pageListParse(document: Document): List<Page> {
|
override fun pageListParse(document: Document): List<Page> {
|
||||||
|
countViews(document)
|
||||||
|
|
||||||
val chapterUrl = document.location()
|
val chapterUrl = document.location()
|
||||||
val htmlPages = document.select(pageSelector)
|
val htmlPages = document.select(pageSelector)
|
||||||
.filterNot { it.imgAttr().isEmpty() }
|
.filterNot { it.imgAttr().isEmpty() }
|
||||||
.mapIndexed { i, img -> Page(i, chapterUrl, img.imgAttr()) }
|
.mapIndexed { i, img -> Page(i, chapterUrl, img.imgAttr()) }
|
||||||
|
|
||||||
countViews(document)
|
|
||||||
|
|
||||||
// Some sites also loads pages via javascript
|
// Some sites also loads pages via javascript
|
||||||
if (htmlPages.isNotEmpty()) { return htmlPages }
|
if (htmlPages.isNotEmpty()) { return htmlPages }
|
||||||
|
|
||||||
|
|
|
@ -1,26 +1,14 @@
|
||||||
package eu.kanade.tachiyomi.extension.es.senpaiediciones
|
package eu.kanade.tachiyomi.extension.es.senpaiediciones
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
|
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
|
||||||
import eu.kanade.tachiyomi.source.model.SManga
|
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
|
||||||
class SenpaiEdiciones : MangaThemesia(
|
class SenpaiEdiciones : MangaThemesia(
|
||||||
"Senpai Ediciones",
|
"Senpai Ediciones",
|
||||||
"http://senpaiediciones.com",
|
"https://senpaiediciones.com",
|
||||||
"es",
|
"es",
|
||||||
dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale("es")),
|
dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale("es")),
|
||||||
) {
|
) {
|
||||||
override val seriesAuthorSelector = ".imptdt:contains(Autor) i"
|
|
||||||
override val seriesStatusSelector = ".imptdt:contains(Estado) i"
|
|
||||||
|
|
||||||
override val pageSelector = "div#readerarea img:not(noscript img)"
|
override val pageSelector = "div#readerarea img:not(noscript img)"
|
||||||
|
|
||||||
override fun String?.parseStatus(): Int = when {
|
|
||||||
this == null -> SManga.UNKNOWN
|
|
||||||
listOf("curso").any { this.contains(it, ignoreCase = true) } -> SManga.ONGOING
|
|
||||||
this.contains("hiatus", ignoreCase = true) -> SManga.ON_HIATUS
|
|
||||||
this.contains("finalizado", ignoreCase = true) -> SManga.COMPLETED
|
|
||||||
else -> SManga.UNKNOWN
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ class CosmicScansID : MangaThemesia("CosmicScans.id", "https://cosmicscans.id",
|
||||||
override val hasProjectPage = true
|
override val hasProjectPage = true
|
||||||
override val projectPageString = "/semua-komik"
|
override val projectPageString = "/semua-komik"
|
||||||
|
|
||||||
override fun latestUpdatesRequest(page: Int) = GET("$baseUrl" + if (page > 1) "/page/$page" else "", headers)
|
override fun latestUpdatesRequest(page: Int) = GET(baseUrl + if (page > 1) "/page/$page" else "", headers)
|
||||||
|
|
||||||
// search
|
// search
|
||||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
||||||
|
@ -47,8 +47,8 @@ class CosmicScansID : MangaThemesia("CosmicScans.id", "https://cosmicscans.id",
|
||||||
val filters = mutableListOf<Filter<*>>(
|
val filters = mutableListOf<Filter<*>>(
|
||||||
Filter.Separator(),
|
Filter.Separator(),
|
||||||
Filter.Header("$name Project List page"),
|
Filter.Header("$name Project List page"),
|
||||||
ProjectFilter(),
|
ProjectFilter(intl["project_filter_title"], projectFilterOptions),
|
||||||
OrderByFilter(),
|
OrderByFilter(intl["order_by_filter_title"], orderByFilterOptions),
|
||||||
)
|
)
|
||||||
return FilterList(filters)
|
return FilterList(filters)
|
||||||
}
|
}
|
||||||
|
|
|
@ -241,12 +241,12 @@ class KomikCast : MangaThemesia("Komik Cast", "https://komikcast.lol", "id", "/d
|
||||||
StatusFilter(),
|
StatusFilter(),
|
||||||
TypeFilter(),
|
TypeFilter(),
|
||||||
OrderByFilter(),
|
OrderByFilter(),
|
||||||
Filter.Header("Genre exclusion is not available for all sources"),
|
Filter.Header(intl["genre_exclusion_warning"]),
|
||||||
GenreListFilter(getGenreList()),
|
GenreListFilter(intl["genre_filter_title"], getGenreList()),
|
||||||
Filter.Separator(),
|
Filter.Separator(),
|
||||||
Filter.Header("NOTE: Can't be used with other filter!"),
|
Filter.Header(intl["project_filter_warning"]),
|
||||||
Filter.Header("$name Project List page"),
|
Filter.Header(intl.format("project_filter_name", name)),
|
||||||
ProjectFilter(),
|
ProjectFilter(intl["project_filter_title"], projectFilterOptions),
|
||||||
)
|
)
|
||||||
return FilterList(filters)
|
return FilterList(filters)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package eu.kanade.tachiyomi.extension.ja.mangamate
|
package eu.kanade.tachiyomi.extension.ja.mangamate
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
|
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
|
||||||
import eu.kanade.tachiyomi.source.model.SManga
|
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
|
||||||
|
@ -12,12 +11,4 @@ class MangaMate : MangaThemesia(
|
||||||
dateFormat = SimpleDateFormat("MMMM d, yyyy", Locale("ja")),
|
dateFormat = SimpleDateFormat("MMMM d, yyyy", Locale("ja")),
|
||||||
) {
|
) {
|
||||||
override val seriesAuthorSelector = ".fmed b:contains(作者) + span"
|
override val seriesAuthorSelector = ".fmed b:contains(作者) + span"
|
||||||
override val seriesStatusSelector = ".tsinfo .imptdt:contains(連載状況) i"
|
|
||||||
|
|
||||||
override fun String?.parseStatus(): Int = when (this) {
|
|
||||||
"連載中" -> SManga.ONGOING
|
|
||||||
"完結" -> SManga.COMPLETED
|
|
||||||
"人気" -> SManga.ON_HIATUS
|
|
||||||
else -> SManga.UNKNOWN
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue