Update TMO, LectorTMO & TMOHentai (#6307)
* Add configurable rate limit, image CDN rate limit and SFW mode. * Add configurable image download mode (cascade/paginated). * Configurable ratelimit and image CDN ratelimit. * Fix redundant title
This commit is contained in:
parent
59b380205f
commit
135e08b754
@ -5,8 +5,12 @@ ext {
|
||||
extName = 'LectorManga'
|
||||
pkgNameSuffix = 'es.lectormanga'
|
||||
extClass = '.LectorManga'
|
||||
extVersionCode = 14
|
||||
extVersionCode = 15
|
||||
libVersion = '1.2'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation project(':lib-ratelimit')
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
@ -2,8 +2,10 @@ package eu.kanade.tachiyomi.extension.es.lectormanga
|
||||
|
||||
import android.app.Application
|
||||
import android.content.SharedPreferences
|
||||
import android.support.v7.preference.CheckBoxPreference
|
||||
import android.support.v7.preference.ListPreference
|
||||
import android.support.v7.preference.PreferenceScreen
|
||||
import eu.kanade.tachiyomi.lib.ratelimit.SpecificHostRateLimitInterceptor
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.network.asObservableSuccess
|
||||
import eu.kanade.tachiyomi.source.ConfigurableSource
|
||||
@ -15,8 +17,8 @@ import eu.kanade.tachiyomi.source.model.SChapter
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
|
||||
import eu.kanade.tachiyomi.util.asJsoup
|
||||
import okhttp3.Headers
|
||||
import okhttp3.HttpUrl
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import org.jsoup.nodes.Document
|
||||
@ -27,9 +29,6 @@ import uy.kohesive.injekt.api.get
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
|
||||
/**
|
||||
* Note: this source is similar to TuMangaOnline.
|
||||
*/
|
||||
class LectorManga : ConfigurableSource, ParsedHttpSource() {
|
||||
|
||||
override val name = "LectorManga"
|
||||
@ -40,14 +39,29 @@ class LectorManga : ConfigurableSource, ParsedHttpSource() {
|
||||
|
||||
override val supportsLatest = true
|
||||
|
||||
private val userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36"
|
||||
private val imageCDNUrl = "https://img1.followmanga.com"
|
||||
|
||||
override fun headersBuilder(): Headers.Builder {
|
||||
return Headers.Builder()
|
||||
.add("User-Agent", userAgent)
|
||||
.add("Referer", "$baseUrl/")
|
||||
private val preferences: SharedPreferences by lazy {
|
||||
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
|
||||
}
|
||||
|
||||
private val webRateLimitInterceptor = SpecificHostRateLimitInterceptor(
|
||||
HttpUrl.parse(baseUrl)!!,
|
||||
preferences.getString(WEB_RATELIMIT_PREF, WEB_RATELIMIT_PREF_DEFAULT_VALUE)!!.toInt(),
|
||||
60
|
||||
)
|
||||
|
||||
private val imageCDNRateLimitInterceptor = SpecificHostRateLimitInterceptor(
|
||||
HttpUrl.parse(imageCDNUrl)!!,
|
||||
preferences.getString(IMAGE_CDN_RATELIMIT_PREF, IMAGE_CDN_RATELIMIT_PREF_DEFAULT_VALUE)!!.toInt(),
|
||||
60
|
||||
)
|
||||
|
||||
override val client: OkHttpClient = network.client.newBuilder()
|
||||
.addNetworkInterceptor(webRateLimitInterceptor)
|
||||
.addNetworkInterceptor(imageCDNRateLimitInterceptor)
|
||||
.build()
|
||||
|
||||
override fun popularMangaRequest(page: Int) = GET("$baseUrl/library?order_item=likes_count&order_dir=desc&type=&filter_by=title&page=$page", headers)
|
||||
|
||||
override fun popularMangaNextPageSelector() = ".pagination .page-item:not(.disabled) a[rel='next']"
|
||||
@ -176,17 +190,16 @@ class LectorManga : ConfigurableSource, ParsedHttpSource() {
|
||||
}
|
||||
|
||||
// Regular list of chapters
|
||||
val dupselect = getduppref()!!
|
||||
val chapterNames = document.select("#chapters h4.text-truncate")
|
||||
val chapterNumbers = chapterNames.map { it.text().substringAfter("Capítulo").substringBefore("|").trim().toFloat() }
|
||||
val chapterInfos = document.select("#chapters .chapter-list")
|
||||
|
||||
chapterNames.forEachIndexed { index, _ ->
|
||||
val scanlator = chapterInfos[index].select("li")
|
||||
if (dupselect == "one") {
|
||||
scanlator.last { add(regularChapterFromElement(chapterNames[index].text(), it, chapterNumbers[index])) }
|
||||
} else {
|
||||
if (getScanlatorPref()) {
|
||||
scanlator.forEach { add(regularChapterFromElement(chapterNames[index].text(), it, chapterNumbers[index])) }
|
||||
} else {
|
||||
scanlator.last { add(regularChapterFromElement(chapterNames[index].text(), it, chapterNumbers[index])) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -224,9 +237,9 @@ class LectorManga : ConfigurableSource, ParsedHttpSource() {
|
||||
val currentUrl = client.newCall(GET(chapter.url, headers)).execute().asJsoup().body().baseUri()
|
||||
|
||||
// Get /cascade instead of /paginate to get all pages at once
|
||||
val newUrl = if (getPageMethod() == "cascade" && currentUrl.contains("paginated")) {
|
||||
val newUrl = if (getPageMethodPref() == "cascade" && currentUrl.contains("paginated")) {
|
||||
currentUrl.substringBefore("paginated") + "cascade"
|
||||
} else if (getPageMethod() == "paginated" && currentUrl.contains("cascade")) {
|
||||
} else if (getPageMethodPref() == "paginated" && currentUrl.contains("cascade")) {
|
||||
currentUrl.substringBefore("cascade") + "paginated"
|
||||
} else currentUrl
|
||||
|
||||
@ -234,7 +247,7 @@ class LectorManga : ConfigurableSource, ParsedHttpSource() {
|
||||
}
|
||||
|
||||
override fun pageListParse(document: Document): List<Page> = mutableListOf<Page>().apply {
|
||||
if (getPageMethod() == "cascade") {
|
||||
if (getPageMethodPref() == "cascade") {
|
||||
document.select("div.viewer-container img").forEach {
|
||||
add(
|
||||
Page(
|
||||
@ -410,91 +423,192 @@ class LectorManga : ConfigurableSource, ParsedHttpSource() {
|
||||
fun toUriPart() = vals[state].second
|
||||
}
|
||||
|
||||
// Preferences
|
||||
|
||||
private val preferences: SharedPreferences by lazy {
|
||||
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
|
||||
}
|
||||
|
||||
override fun setupPreferenceScreen(screen: androidx.preference.PreferenceScreen) {
|
||||
val deduppref = androidx.preference.ListPreference(screen.context).apply {
|
||||
key = DEDUP_PREF_Title
|
||||
title = DEDUP_PREF_Title
|
||||
entries = arrayOf("Mostrar todos los scanlators", "Mostrar solo un scanlator")
|
||||
entryValues = arrayOf("all", "one")
|
||||
summary = "%s"
|
||||
|
||||
val scanlatorPref = androidx.preference.CheckBoxPreference(screen.context).apply {
|
||||
key = SCANLATOR_PREF
|
||||
title = SCANLATOR_PREF_TITLE
|
||||
summary = SCANLATOR_PREF_SUMMARY
|
||||
setDefaultValue(SCANLATOR_PREF_DEFAULT_VALUE)
|
||||
|
||||
setOnPreferenceChangeListener { _, newValue ->
|
||||
val selected = newValue as String
|
||||
val index = this.findIndexOfValue(selected)
|
||||
val entry = entryValues[index] as String
|
||||
preferences.edit().putString(DEDUP_PREF, entry).commit()
|
||||
val checkValue = newValue as Boolean
|
||||
preferences.edit().putBoolean(SCANLATOR_PREF, checkValue).commit()
|
||||
}
|
||||
}
|
||||
|
||||
val pageMethod = androidx.preference.ListPreference(screen.context).apply {
|
||||
key = PAGEGET_PREF_Title
|
||||
title = PAGEGET_PREF_Title
|
||||
entries = arrayOf("Cascada (recomendado)", "Paginado")
|
||||
val pageMethodPref = androidx.preference.ListPreference(screen.context).apply {
|
||||
key = PAGE_METHOD_PREF
|
||||
title = PAGE_METHOD_PREF_TITLE
|
||||
entries = arrayOf("Cascada", "Páginado")
|
||||
entryValues = arrayOf("cascade", "paginated")
|
||||
summary = "%s"
|
||||
summary = PAGE_METHOD_PREF_SUMMARY
|
||||
setDefaultValue(PAGE_METHOD_PREF_DEFAULT_VALUE)
|
||||
|
||||
setOnPreferenceChangeListener { _, newValue ->
|
||||
val selected = newValue as String
|
||||
val index = this.findIndexOfValue(selected)
|
||||
val entry = entryValues[index] as String
|
||||
preferences.edit().putString(PAGEGET_PREF, entry).commit()
|
||||
try {
|
||||
val setting = preferences.edit().putString(PAGE_METHOD_PREF, newValue as String).commit()
|
||||
setting
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
screen.addPreference(deduppref)
|
||||
screen.addPreference(pageMethod)
|
||||
// Rate limit
|
||||
val apiRateLimitPreference = androidx.preference.ListPreference(screen.context).apply {
|
||||
key = WEB_RATELIMIT_PREF
|
||||
title = WEB_RATELIMIT_PREF_TITLE
|
||||
summary = WEB_RATELIMIT_PREF_SUMMARY
|
||||
entries = ENTRIES_ARRAY
|
||||
entryValues = ENTRIES_ARRAY
|
||||
|
||||
setDefaultValue(WEB_RATELIMIT_PREF_DEFAULT_VALUE)
|
||||
setOnPreferenceChangeListener { _, newValue ->
|
||||
try {
|
||||
val setting = preferences.edit().putString(WEB_RATELIMIT_PREF, newValue as String).commit()
|
||||
setting
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val imgCDNRateLimitPreference = androidx.preference.ListPreference(screen.context).apply {
|
||||
key = IMAGE_CDN_RATELIMIT_PREF
|
||||
title = IMAGE_CDN_RATELIMIT_PREF_TITLE
|
||||
summary = IMAGE_CDN_RATELIMIT_PREF_SUMMARY
|
||||
entries = ENTRIES_ARRAY
|
||||
entryValues = ENTRIES_ARRAY
|
||||
|
||||
setDefaultValue(IMAGE_CDN_RATELIMIT_PREF_DEFAULT_VALUE)
|
||||
setOnPreferenceChangeListener { _, newValue ->
|
||||
try {
|
||||
val setting = preferences.edit().putString(IMAGE_CDN_RATELIMIT_PREF, newValue as String).commit()
|
||||
setting
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
screen.addPreference(scanlatorPref)
|
||||
screen.addPreference(pageMethodPref)
|
||||
screen.addPreference(apiRateLimitPreference)
|
||||
screen.addPreference(imgCDNRateLimitPreference)
|
||||
}
|
||||
|
||||
override fun setupPreferenceScreen(screen: PreferenceScreen) {
|
||||
val deduppref = ListPreference(screen.context).apply {
|
||||
key = DEDUP_PREF_Title
|
||||
title = DEDUP_PREF_Title
|
||||
entries = arrayOf("Mostrar todos los scanlators", "Mostrar solo un scanlator")
|
||||
entryValues = arrayOf("all", "one")
|
||||
summary = "%s"
|
||||
|
||||
val scanlatorPref = CheckBoxPreference(screen.context).apply {
|
||||
key = SCANLATOR_PREF
|
||||
title = SCANLATOR_PREF_TITLE
|
||||
summary = SCANLATOR_PREF_SUMMARY
|
||||
setDefaultValue(SCANLATOR_PREF_DEFAULT_VALUE)
|
||||
|
||||
setOnPreferenceChangeListener { _, newValue ->
|
||||
val selected = newValue as String
|
||||
val index = this.findIndexOfValue(selected)
|
||||
val entry = entryValues[index] as String
|
||||
preferences.edit().putString(DEDUP_PREF, entry).commit()
|
||||
val checkValue = newValue as Boolean
|
||||
preferences.edit().putBoolean(SCANLATOR_PREF, checkValue).commit()
|
||||
}
|
||||
}
|
||||
|
||||
val pageMethod = ListPreference(screen.context).apply {
|
||||
key = PAGEGET_PREF_Title
|
||||
title = PAGEGET_PREF_Title
|
||||
entries = arrayOf("Cascada (recomendado)", "Paginado")
|
||||
val pageMethodPref = ListPreference(screen.context).apply {
|
||||
key = PAGE_METHOD_PREF
|
||||
title = PAGE_METHOD_PREF_TITLE
|
||||
entries = arrayOf("Cascada", "Páginado")
|
||||
entryValues = arrayOf("cascade", "paginated")
|
||||
summary = "%s"
|
||||
summary = PAGE_METHOD_PREF_SUMMARY
|
||||
setDefaultValue(PAGE_METHOD_PREF_DEFAULT_VALUE)
|
||||
|
||||
setOnPreferenceChangeListener { _, newValue ->
|
||||
val selected = newValue as String
|
||||
val index = this.findIndexOfValue(selected)
|
||||
val entry = entryValues[index] as String
|
||||
preferences.edit().putString(PAGEGET_PREF, entry).commit()
|
||||
try {
|
||||
val setting = preferences.edit().putString(PAGE_METHOD_PREF, newValue as String).commit()
|
||||
setting
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
screen.addPreference(deduppref)
|
||||
screen.addPreference(pageMethod)
|
||||
// Rate limit
|
||||
val apiRateLimitPreference = ListPreference(screen.context).apply {
|
||||
key = WEB_RATELIMIT_PREF
|
||||
title = WEB_RATELIMIT_PREF_TITLE
|
||||
summary = WEB_RATELIMIT_PREF_SUMMARY
|
||||
entries = ENTRIES_ARRAY
|
||||
entryValues = ENTRIES_ARRAY
|
||||
|
||||
setDefaultValue(WEB_RATELIMIT_PREF_DEFAULT_VALUE)
|
||||
setOnPreferenceChangeListener { _, newValue ->
|
||||
try {
|
||||
val setting = preferences.edit().putString(WEB_RATELIMIT_PREF, newValue as String).commit()
|
||||
setting
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val imgCDNRateLimitPreference = ListPreference(screen.context).apply {
|
||||
key = IMAGE_CDN_RATELIMIT_PREF
|
||||
title = IMAGE_CDN_RATELIMIT_PREF_TITLE
|
||||
summary = IMAGE_CDN_RATELIMIT_PREF_SUMMARY
|
||||
entries = ENTRIES_ARRAY
|
||||
entryValues = ENTRIES_ARRAY
|
||||
|
||||
setDefaultValue(IMAGE_CDN_RATELIMIT_PREF_DEFAULT_VALUE)
|
||||
setOnPreferenceChangeListener { _, newValue ->
|
||||
try {
|
||||
val setting = preferences.edit().putString(IMAGE_CDN_RATELIMIT_PREF, newValue as String).commit()
|
||||
setting
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
screen.addPreference(scanlatorPref)
|
||||
screen.addPreference(pageMethodPref)
|
||||
screen.addPreference(apiRateLimitPreference)
|
||||
screen.addPreference(imgCDNRateLimitPreference)
|
||||
}
|
||||
|
||||
private fun getduppref() = preferences.getString(DEDUP_PREF, "all")
|
||||
private fun getScanlatorPref(): Boolean = preferences.getBoolean(SCANLATOR_PREF, SCANLATOR_PREF_DEFAULT_VALUE)
|
||||
|
||||
private fun getPageMethod() = preferences.getString(PAGEGET_PREF, "cascade")
|
||||
private fun getPageMethodPref() = preferences.getString(PAGE_METHOD_PREF, PAGE_METHOD_PREF_DEFAULT_VALUE)
|
||||
|
||||
companion object {
|
||||
private const val DEDUP_PREF_Title = "Preferencias de scanlator"
|
||||
private const val DEDUP_PREF = "deduppref"
|
||||
private const val PAGEGET_PREF_Title = "Método para la descarga de imágenes"
|
||||
private const val PAGEGET_PREF = "pagemethodpref"
|
||||
private const val SCANLATOR_PREF = "scanlatorPref"
|
||||
private const val SCANLATOR_PREF_TITLE = "Mostrar todos los scanlator"
|
||||
private const val SCANLATOR_PREF_SUMMARY = "Se mostraran capítulos repetidos pero con diferentes Scanlators"
|
||||
private const val SCANLATOR_PREF_DEFAULT_VALUE = true
|
||||
|
||||
private const val PAGE_METHOD_PREF = "pageMethodPref"
|
||||
private const val PAGE_METHOD_PREF_TITLE = "Método para descargar imágenes"
|
||||
private const val PAGE_METHOD_PREF_SUMMARY = "Previene ser banneado por el servidor cuando se usa la configuración \"Cascada\" ya que esta reduce la cantidad de solicitudes.\nPuedes usar \"Páginado\" cuando las imágenes no carguen usando \"Cascada\".\nConfiguración actual: %s"
|
||||
private const val PAGE_METHOD_PREF_DEFAULT_VALUE = "cascade"
|
||||
|
||||
private const val WEB_RATELIMIT_PREF = "webRatelimitPreference"
|
||||
// Ratelimit permits per second for main website
|
||||
private const val WEB_RATELIMIT_PREF_TITLE = "Ratelimit por minuto para el sitio web"
|
||||
// This value affects network request amount to TMO url. Lower this value may reduce the chance to get HTTP 429 error, but loading speed will be slower too. Tachiyomi restart required. \nCurrent value: %s
|
||||
private const val WEB_RATELIMIT_PREF_SUMMARY = "Este valor afecta la cantidad de solicitudes de red a la URL de TMO. Reducir este valor puede disminuir la posibilidad de obtener un error HTTP 429, pero la velocidad de descarga será más lenta. Se requiere reiniciar Tachiyomi. \nValor actual: %s"
|
||||
private const val WEB_RATELIMIT_PREF_DEFAULT_VALUE = "10"
|
||||
|
||||
private const val IMAGE_CDN_RATELIMIT_PREF = "imgCDNRatelimitPreference"
|
||||
// Ratelimit permits per second for image CDN
|
||||
private const val IMAGE_CDN_RATELIMIT_PREF_TITLE = "Ratelimit por minuto para descarga de imágenes"
|
||||
// This value affects network request amount for loading image. Lower this value may reduce the chance to get error when loading image, but loading speed will be slower too. Tachiyomi restart required. \nCurrent value: %s
|
||||
private const val IMAGE_CDN_RATELIMIT_PREF_SUMMARY = "Este valor afecta la cantidad de solicitudes de red para descargar imágenes. Reducir este valor puede disminuir errores al cargar imagenes, pero la velocidad de descarga será más lenta. Se requiere reiniciar Tachiyomi. \nValor actual: %s"
|
||||
private const val IMAGE_CDN_RATELIMIT_PREF_DEFAULT_VALUE = "10"
|
||||
|
||||
private val ENTRIES_ARRAY = (1..10).map { i -> i.toString() }.toTypedArray()
|
||||
|
||||
const val PREFIX_ID_SEARCH = "id:"
|
||||
const val MANGA_URL_CHUNK = "gotobook"
|
||||
|
@ -5,7 +5,7 @@ ext {
|
||||
extName = 'TMOHentai'
|
||||
pkgNameSuffix = 'es.tmohentai'
|
||||
extClass = '.TMOHentai'
|
||||
extVersionCode = 4
|
||||
extVersionCode = 5
|
||||
libVersion = '1.2'
|
||||
containsNsfw = true
|
||||
}
|
||||
|
@ -1,8 +1,13 @@
|
||||
package eu.kanade.tachiyomi.extension.es.tmohentai
|
||||
|
||||
import android.app.Application
|
||||
import android.content.SharedPreferences
|
||||
import android.support.v7.preference.ListPreference
|
||||
import android.support.v7.preference.PreferenceScreen
|
||||
import eu.kanade.tachiyomi.annotations.Nsfw
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.network.asObservableSuccess
|
||||
import eu.kanade.tachiyomi.source.ConfigurableSource
|
||||
import eu.kanade.tachiyomi.source.model.Filter
|
||||
import eu.kanade.tachiyomi.source.model.FilterList
|
||||
import eu.kanade.tachiyomi.source.model.MangasPage
|
||||
@ -15,9 +20,11 @@ import okhttp3.Request
|
||||
import org.jsoup.nodes.Document
|
||||
import org.jsoup.nodes.Element
|
||||
import rx.Observable
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
||||
@Nsfw
|
||||
class TMOHentai : ParsedHttpSource() {
|
||||
class TMOHentai : ConfigurableSource, ParsedHttpSource() {
|
||||
|
||||
override val name = "TMOHentai"
|
||||
|
||||
@ -27,6 +34,10 @@ class TMOHentai : ParsedHttpSource() {
|
||||
|
||||
override val supportsLatest = true
|
||||
|
||||
private val preferences: SharedPreferences by lazy {
|
||||
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
|
||||
}
|
||||
|
||||
override fun popularMangaRequest(page: Int) = GET("$baseUrl/section/all?view=list&page=$page&order=popularity&order-dir=desc&search[searchText]=&search[searchBy]=name&type=all", headers)
|
||||
|
||||
override fun popularMangaSelector() = "table > tbody > tr[data-toggle=popover]"
|
||||
@ -71,19 +82,47 @@ class TMOHentai : ParsedHttpSource() {
|
||||
|
||||
name = element.select("h3.truncate").text()
|
||||
scanlator = parsedInformation.substringAfter("By").substringBefore("Language").trim()
|
||||
setUrlWithoutDomain(element.select("a.pull-right.btn.btn-primary").attr("href"))
|
||||
|
||||
var currentUrl = element.select("a.pull-right.btn.btn-primary").attr("href")
|
||||
|
||||
if (currentUrl.contains("/1")) {
|
||||
currentUrl = currentUrl.substringBeforeLast("/")
|
||||
}
|
||||
|
||||
setUrlWithoutDomain(currentUrl)
|
||||
// date_upload = no date in the web
|
||||
}
|
||||
|
||||
override fun pageListRequest(chapter: SChapter) = GET(baseUrl + chapter.url.substringBefore("/paginated") + "/cascade", headers) // "/cascade" to get all images
|
||||
// "/cascade" to get all images
|
||||
override fun pageListRequest(chapter: SChapter): Request {
|
||||
val currentUrl = chapter.url
|
||||
val newUrl = if (getPageMethodPref() == "cascade" && currentUrl.contains("paginated")) {
|
||||
currentUrl.substringBefore("paginated") + "cascade"
|
||||
} else if (getPageMethodPref() == "paginated" && currentUrl.contains("cascade")) {
|
||||
currentUrl.substringBefore("cascade") + "paginated"
|
||||
} else currentUrl
|
||||
|
||||
return GET("$baseUrl$newUrl", headers)
|
||||
}
|
||||
|
||||
override fun pageListParse(document: Document): List<Page> = mutableListOf<Page>().apply {
|
||||
document.select("div#content-images > div.row > div.col-xs-12.text-center > img.content-image")?.forEach {
|
||||
add(Page(size, "", baseUrl + it.attr("data-original")))
|
||||
if (getPageMethodPref() == "cascade") {
|
||||
document.select("div#content-images img.content-image")?.forEach {
|
||||
add(Page(size, "", it.attr("data-original")))
|
||||
}
|
||||
} else {
|
||||
val pageList = document.select("select#select-page").first().select("option").map { it.attr("value").toInt() }
|
||||
val url = document.baseUri()
|
||||
|
||||
pageList.forEach {
|
||||
add(Page(it, "$url/$it"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun imageUrlParse(document: Document) = throw UnsupportedOperationException("Not used")
|
||||
override fun imageUrlParse(document: Document) = document.select("div#content-images img.content-image").attr("data-original")
|
||||
|
||||
override fun imageRequest(page: Page) = GET("$baseUrl${page.imageUrl!!}", headers)
|
||||
|
||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
||||
val url = HttpUrl.parse("$baseUrl/section/all?view=list")!!.newBuilder()
|
||||
@ -193,9 +232,13 @@ class TMOHentai : ParsedHttpSource() {
|
||||
Selection(2, false)
|
||||
)
|
||||
|
||||
// Array.from(document.querySelectorAll('#advancedSearch .list-group .list-group-item'))
|
||||
// .map(a => `Genre("${a.querySelector('span').innerText.replace(' ', '')}", "${a.querySelector('input').value}")`).join(',\n')
|
||||
// https://tmohentai.com/section/hentai
|
||||
/**
|
||||
* Last check: 17/02/2021
|
||||
* https://tmohentai.com/section/hentai
|
||||
*
|
||||
* Array.from(document.querySelectorAll('#advancedSearch .list-group .list-group-item'))
|
||||
* .map(a => `Genre("${a.querySelector('span').innerText.replace(' ', '')}", "${a.querySelector('input').value}")`).join(',\n')
|
||||
*/
|
||||
private fun getGenreList() = listOf(
|
||||
Genre("Romance", "1"),
|
||||
Genre("Fantasy", "2"),
|
||||
@ -245,7 +288,62 @@ class TMOHentai : ParsedHttpSource() {
|
||||
Genre("Yandere", "46")
|
||||
)
|
||||
|
||||
override fun setupPreferenceScreen(screen: androidx.preference.PreferenceScreen) {
|
||||
val pageMethodPref = androidx.preference.ListPreference(screen.context).apply {
|
||||
key = PAGE_METHOD_PREF
|
||||
title = PAGE_METHOD_PREF_TITLE
|
||||
entries = arrayOf("Cascada", "Páginado")
|
||||
entryValues = arrayOf("cascade", "paginated")
|
||||
summary = PAGE_METHOD_PREF_SUMMARY
|
||||
setDefaultValue(PAGE_METHOD_PREF_DEFAULT_VALUE)
|
||||
|
||||
setOnPreferenceChangeListener { _, newValue ->
|
||||
try {
|
||||
val setting = preferences.edit().putString(PAGE_METHOD_PREF, newValue as String).commit()
|
||||
setting
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
screen.addPreference(pageMethodPref)
|
||||
}
|
||||
|
||||
override fun setupPreferenceScreen(screen: PreferenceScreen) {
|
||||
val pageMethodPref = ListPreference(screen.context).apply {
|
||||
key = PAGE_METHOD_PREF
|
||||
title = PAGE_METHOD_PREF_TITLE
|
||||
entries = arrayOf("Cascada", "Páginado")
|
||||
entryValues = arrayOf(PAGE_METHOD_PREF_CASCADE, PAGE_METHOD_PREF_PAGINATED)
|
||||
summary = PAGE_METHOD_PREF_SUMMARY
|
||||
setDefaultValue(PAGE_METHOD_PREF_DEFAULT_VALUE)
|
||||
|
||||
setOnPreferenceChangeListener { _, newValue ->
|
||||
try {
|
||||
val setting = preferences.edit().putString(PAGE_METHOD_PREF, newValue as String).commit()
|
||||
setting
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
screen.addPreference(pageMethodPref)
|
||||
}
|
||||
|
||||
private fun getPageMethodPref() = preferences.getString(PAGE_METHOD_PREF, PAGE_METHOD_PREF_DEFAULT_VALUE)
|
||||
|
||||
companion object {
|
||||
private const val PAGE_METHOD_PREF = "pageMethodPref"
|
||||
private const val PAGE_METHOD_PREF_TITLE = "Método de descarga de imágenes"
|
||||
private const val PAGE_METHOD_PREF_SUMMARY = "Puede corregir errores al cargar las imágenes.\nConfiguración actual: %s"
|
||||
private const val PAGE_METHOD_PREF_CASCADE = "cascade"
|
||||
private const val PAGE_METHOD_PREF_PAGINATED = "paginated"
|
||||
private const val PAGE_METHOD_PREF_DEFAULT_VALUE = PAGE_METHOD_PREF_CASCADE
|
||||
|
||||
const val PREFIX_CONTENTS = "contents"
|
||||
const val PREFIX_ID_SEARCH = "id:"
|
||||
|
||||
|
@ -5,7 +5,7 @@ ext {
|
||||
extName = 'TuMangaOnline'
|
||||
pkgNameSuffix = 'es.tumangaonline'
|
||||
extClass = '.TuMangaOnline'
|
||||
extVersionCode = 31
|
||||
extVersionCode = 32
|
||||
libVersion = '1.2'
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.extension.es.tumangaonline
|
||||
|
||||
import android.app.Application
|
||||
import android.content.SharedPreferences
|
||||
import android.support.v7.preference.CheckBoxPreference
|
||||
import android.support.v7.preference.ListPreference
|
||||
import android.support.v7.preference.PreferenceScreen
|
||||
import eu.kanade.tachiyomi.lib.ratelimit.SpecificHostRateLimitInterceptor
|
||||
@ -16,7 +17,6 @@ import eu.kanade.tachiyomi.source.model.SChapter
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
|
||||
import eu.kanade.tachiyomi.util.asJsoup
|
||||
import okhttp3.Headers
|
||||
import okhttp3.HttpUrl
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
@ -35,28 +35,37 @@ class TuMangaOnline : ConfigurableSource, ParsedHttpSource() {
|
||||
|
||||
override val baseUrl = "https://lectortmo.com"
|
||||
|
||||
val baseurl = HttpUrl.parse(baseUrl)!!
|
||||
|
||||
override val lang = "es"
|
||||
|
||||
override val supportsLatest = true
|
||||
|
||||
private val userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36"
|
||||
private val imageCDNUrl = "https://img1.japanreader.com"
|
||||
|
||||
private val rateLimitInterceptor = SpecificHostRateLimitInterceptor(baseurl, 10, 60) // real ratelimit values
|
||||
|
||||
override val client: OkHttpClient = network.client.newBuilder()
|
||||
.addNetworkInterceptor(rateLimitInterceptor)
|
||||
.build()
|
||||
|
||||
override fun headersBuilder(): Headers.Builder {
|
||||
return Headers.Builder()
|
||||
.add("User-Agent", userAgent)
|
||||
.add("Referer", "$baseUrl/")
|
||||
.add("Cache-mode", "no-cache")
|
||||
private val preferences: SharedPreferences by lazy {
|
||||
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
|
||||
}
|
||||
|
||||
override fun popularMangaRequest(page: Int) = GET("$baseUrl/library?order_item=likes_count&order_dir=desc&filter_by=title&_page=1&page=$page", headers)
|
||||
private val webRateLimitInterceptor = SpecificHostRateLimitInterceptor(
|
||||
HttpUrl.parse(baseUrl)!!,
|
||||
preferences.getString(WEB_RATELIMIT_PREF, WEB_RATELIMIT_PREF_DEFAULT_VALUE)!!.toInt(),
|
||||
60
|
||||
)
|
||||
|
||||
private val imageCDNRateLimitInterceptor = SpecificHostRateLimitInterceptor(
|
||||
HttpUrl.parse(imageCDNUrl)!!,
|
||||
preferences.getString(IMAGE_CDN_RATELIMIT_PREF, IMAGE_CDN_RATELIMIT_PREF_DEFAULT_VALUE)!!.toInt(),
|
||||
60
|
||||
)
|
||||
|
||||
override val client: OkHttpClient = network.client.newBuilder()
|
||||
.addNetworkInterceptor(webRateLimitInterceptor)
|
||||
.addNetworkInterceptor(imageCDNRateLimitInterceptor)
|
||||
.build()
|
||||
|
||||
// Marks erotic content as false and excludes: Ecchi(6), GirlsLove(17), BoysLove(18) and Harem(19) genders
|
||||
private val getSFWUrlPart = if (getSFWModePref()) "&exclude_genders%5B%5D=6&exclude_genders%5B%5D=17&exclude_genders%5B%5D=18&exclude_genders%5B%5D=19&erotic=false" else ""
|
||||
|
||||
override fun popularMangaRequest(page: Int) = GET("$baseUrl/library?order_item=likes_count&order_dir=desc&filter_by=title$getSFWUrlPart&_page=1&page=$page", headers)
|
||||
|
||||
override fun popularMangaNextPageSelector() = "a.page-link"
|
||||
|
||||
@ -70,7 +79,7 @@ class TuMangaOnline : ConfigurableSource, ParsedHttpSource() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/library?order_item=creation&order_dir=desc&filter_by=title&_page=1&page=$page", headers)
|
||||
override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/library?order_item=creation&order_dir=desc&filter_by=title$getSFWUrlPart&_page=1&page=$page", headers)
|
||||
|
||||
override fun latestUpdatesNextPageSelector() = popularMangaNextPageSelector()
|
||||
|
||||
@ -80,11 +89,15 @@ class TuMangaOnline : ConfigurableSource, ParsedHttpSource() {
|
||||
|
||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
||||
val url = HttpUrl.parse("$baseUrl/library")!!.newBuilder()
|
||||
|
||||
url.addQueryParameter("title", query)
|
||||
if (getSFWModePref()) {
|
||||
SFW_MODE_PREF_EXCLUDE_GENDERS.forEach { gender ->
|
||||
url.addQueryParameter("exclude_genders[]", gender)
|
||||
}
|
||||
url.addQueryParameter("erotic", "false")
|
||||
}
|
||||
url.addQueryParameter("page", page.toString())
|
||||
url.addQueryParameter("_page", "1") // Extra Query to Prevent Scrapping aka without it = 403
|
||||
|
||||
filters.forEach { filter ->
|
||||
when (filter) {
|
||||
is Types -> {
|
||||
@ -93,6 +106,12 @@ class TuMangaOnline : ConfigurableSource, ParsedHttpSource() {
|
||||
is Demography -> {
|
||||
url.addQueryParameter("demography", filter.toUriPart())
|
||||
}
|
||||
is Status -> {
|
||||
url.addQueryParameter("status", filter.toUriPart())
|
||||
}
|
||||
is TranslationStatus -> {
|
||||
url.addQueryParameter("translation_status", filter.toUriPart())
|
||||
}
|
||||
is FilterBy -> {
|
||||
url.addQueryParameter("filter_by", filter.toUriPart())
|
||||
}
|
||||
@ -105,93 +124,58 @@ class TuMangaOnline : ConfigurableSource, ParsedHttpSource() {
|
||||
)
|
||||
}
|
||||
}
|
||||
is WebcomicFilter -> {
|
||||
url.addQueryParameter(
|
||||
"webcomic",
|
||||
when (filter.state) {
|
||||
Filter.TriState.STATE_INCLUDE -> "true"
|
||||
Filter.TriState.STATE_EXCLUDE -> "false"
|
||||
else -> ""
|
||||
is ContentTypeList -> {
|
||||
filter.state.forEach { content ->
|
||||
// If (SFW mode is not enabled) OR (SFW mode is enabled AND filter != erotic) -> Apply filter
|
||||
// else -> ignore filter
|
||||
if (!getSFWModePref() || (getSFWModePref() && content.id != "erotic")) {
|
||||
when (content.state) {
|
||||
Filter.TriState.STATE_IGNORE -> url.addQueryParameter(content.id, "")
|
||||
Filter.TriState.STATE_INCLUDE -> url.addQueryParameter(content.id, "true")
|
||||
Filter.TriState.STATE_EXCLUDE -> url.addQueryParameter(content.id, "false")
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
is FourKomaFilter -> {
|
||||
url.addQueryParameter(
|
||||
"yonkoma",
|
||||
when (filter.state) {
|
||||
Filter.TriState.STATE_INCLUDE -> "true"
|
||||
Filter.TriState.STATE_EXCLUDE -> "false"
|
||||
else -> ""
|
||||
}
|
||||
)
|
||||
}
|
||||
is AmateurFilter -> {
|
||||
url.addQueryParameter(
|
||||
"amateur",
|
||||
when (filter.state) {
|
||||
Filter.TriState.STATE_INCLUDE -> "true"
|
||||
Filter.TriState.STATE_EXCLUDE -> "false"
|
||||
else -> ""
|
||||
}
|
||||
)
|
||||
}
|
||||
is EroticFilter -> {
|
||||
url.addQueryParameter(
|
||||
"erotic",
|
||||
when (filter.state) {
|
||||
Filter.TriState.STATE_INCLUDE -> "true"
|
||||
Filter.TriState.STATE_EXCLUDE -> "false"
|
||||
else -> ""
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
is GenreList -> {
|
||||
filter.state
|
||||
.filter { genre -> genre.state }
|
||||
.forEach { genre -> url.addQueryParameter("genders[]", genre.id) }
|
||||
filter.state.forEach { genre ->
|
||||
when (genre.state) {
|
||||
Filter.TriState.STATE_INCLUDE -> url.addQueryParameter("exclude_genders[]", genre.id)
|
||||
Filter.TriState.STATE_EXCLUDE -> url.addQueryParameter("genders[]", genre.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return GET(url.build().toString(), headers)
|
||||
}
|
||||
|
||||
override fun searchMangaSelector() = popularMangaSelector()
|
||||
|
||||
override fun searchMangaNextPageSelector() = popularMangaNextPageSelector()
|
||||
|
||||
override fun searchMangaFromElement(element: Element): SManga = popularMangaFromElement(element)
|
||||
|
||||
override fun mangaDetailsParse(document: Document) = SManga.create().apply {
|
||||
title = document.select("h2.element-subtitle").text()
|
||||
document.select("h5.card-title").let {
|
||||
author = it?.first()?.attr("title")?.substringAfter(", ")
|
||||
artist = it?.last()?.attr("title")?.substringAfter(", ")
|
||||
}
|
||||
|
||||
genre = document.select("a.py-2").joinToString(", ") {
|
||||
it.text()
|
||||
}
|
||||
|
||||
description = document.select("p.element-description")?.text()
|
||||
status = parseStatus(document.select("span.book-status")?.text().orEmpty())
|
||||
thumbnail_url = document.select(".book-thumbnail").attr("src")
|
||||
}
|
||||
|
||||
private fun parseStatus(status: String) = when {
|
||||
status.contains("Publicándose") -> SManga.ONGOING
|
||||
status.contains("Finalizado") -> SManga.COMPLETED
|
||||
else -> SManga.UNKNOWN
|
||||
}
|
||||
|
||||
override fun chapterListParse(response: Response): List<SChapter> {
|
||||
val document = response.asJsoup()
|
||||
|
||||
// One-shot
|
||||
if (document.select("div.chapters").isEmpty()) {
|
||||
return document.select(oneShotChapterListSelector()).map { oneShotChapterFromElement(it) }
|
||||
}
|
||||
|
||||
// Regular list of chapters
|
||||
val chapters = mutableListOf<SChapter>()
|
||||
document.select(regularChapterListSelector()).forEach { chapelement ->
|
||||
@ -202,22 +186,17 @@ class TuMangaOnline : ConfigurableSource, ParsedHttpSource() {
|
||||
.toFloat()
|
||||
val chaptername = chapelement.select("div.col-10.text-truncate").text()
|
||||
val scanelement = chapelement.select("ul.chapter-list > li")
|
||||
val dupselect = getduppref()!!
|
||||
if (dupselect == "one") {
|
||||
scanelement.first { chapters.add(regularChapterFromElement(it, chaptername, chapternumber)) }
|
||||
} else {
|
||||
if (getScanlatorPref()) {
|
||||
scanelement.forEach { chapters.add(regularChapterFromElement(it, chaptername, chapternumber)) }
|
||||
} else {
|
||||
scanelement.first { chapters.add(regularChapterFromElement(it, chaptername, chapternumber)) }
|
||||
}
|
||||
}
|
||||
return chapters
|
||||
}
|
||||
|
||||
override fun chapterListSelector() = throw UnsupportedOperationException("Not used")
|
||||
|
||||
override fun chapterFromElement(element: Element) = throw UnsupportedOperationException("Not used")
|
||||
|
||||
private fun oneShotChapterListSelector() = "div.chapter-list-element > ul.list-group li.list-group-item"
|
||||
|
||||
private fun oneShotChapterFromElement(element: Element) = SChapter.create().apply {
|
||||
url = element.select("div.row > .text-right > a").attr("href")
|
||||
name = "One Shot"
|
||||
@ -225,9 +204,7 @@ class TuMangaOnline : ConfigurableSource, ParsedHttpSource() {
|
||||
date_upload = element.select("span.badge.badge-primary.p-2").first()?.text()?.let { parseChapterDate(it) }
|
||||
?: 0
|
||||
}
|
||||
|
||||
private fun regularChapterListSelector() = "div.chapters > ul.list-group li.p-0.list-group-item"
|
||||
|
||||
private fun regularChapterFromElement(element: Element, chName: String, number: Float) = SChapter.create().apply {
|
||||
url = element.select("div.row > .text-right > a").attr("href")
|
||||
name = chName
|
||||
@ -236,25 +213,20 @@ class TuMangaOnline : ConfigurableSource, ParsedHttpSource() {
|
||||
date_upload = element.select("span.badge.badge-primary.p-2").first()?.text()?.let { parseChapterDate(it) }
|
||||
?: 0
|
||||
}
|
||||
|
||||
private fun parseChapterDate(date: String): Long = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).parse(date)?.time
|
||||
?: 0
|
||||
|
||||
override fun pageListRequest(chapter: SChapter): Request {
|
||||
val currentUrl = client.newCall(GET(chapter.url, headers)).execute().asJsoup().body().baseUri()
|
||||
|
||||
// Get /cascade instead of /paginate to get all pages at once
|
||||
val newUrl = if (getPageMethod() == "cascade" && currentUrl.contains("paginated")) {
|
||||
val newUrl = if (getPageMethodPref() == "cascade" && currentUrl.contains("paginated")) {
|
||||
currentUrl.substringBefore("paginated") + "cascade"
|
||||
} else if (getPageMethod() == "paginated" && currentUrl.contains("cascade")) {
|
||||
} else if (getPageMethodPref() == "paginated" && currentUrl.contains("cascade")) {
|
||||
currentUrl.substringBefore("cascade") + "paginated"
|
||||
} else currentUrl
|
||||
|
||||
return GET(newUrl, headers)
|
||||
}
|
||||
|
||||
override fun pageListParse(document: Document): List<Page> = mutableListOf<Page>().apply {
|
||||
if (getPageMethod() == "cascade") {
|
||||
if (getPageMethodPref() == "cascade") {
|
||||
document.select("div.viewer-container img").forEach {
|
||||
add(
|
||||
Page(
|
||||
@ -275,8 +247,7 @@ class TuMangaOnline : ConfigurableSource, ParsedHttpSource() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Note: At this moment (13/07/2020) it's necessary to make the image request without headers to prevent 403.
|
||||
// Note: At this moment (15/02/2021) it's necessary to make the image request without headers to prevent 403.
|
||||
override fun imageRequest(page: Page) = GET(page.imageUrl!!)
|
||||
|
||||
override fun imageUrlParse(document: Document): String {
|
||||
@ -319,6 +290,27 @@ class TuMangaOnline : ConfigurableSource, ParsedHttpSource() {
|
||||
)
|
||||
)
|
||||
|
||||
private class Status : UriPartFilter(
|
||||
"Filtrar por estado de serie",
|
||||
arrayOf(
|
||||
Pair("Ver todo", ""),
|
||||
Pair("Publicándose", "publishing"),
|
||||
Pair("Finalizado", "ended"),
|
||||
Pair("Cancelado", "cancelled"),
|
||||
Pair("Pausado", "on_hold")
|
||||
)
|
||||
)
|
||||
|
||||
private class TranslationStatus : UriPartFilter(
|
||||
"Filtrar por estado de traducción",
|
||||
arrayOf(
|
||||
Pair("Ver todo", ""),
|
||||
Pair("Activo", "publishing"),
|
||||
Pair("Finalizado", "ended"),
|
||||
Pair("Abandonado", "cancelled")
|
||||
)
|
||||
)
|
||||
|
||||
private class Demography : UriPartFilter(
|
||||
"Filtrar por demografía",
|
||||
arrayOf(
|
||||
@ -332,7 +324,7 @@ class TuMangaOnline : ConfigurableSource, ParsedHttpSource() {
|
||||
)
|
||||
|
||||
private class FilterBy : UriPartFilter(
|
||||
"Campo de orden",
|
||||
"Filtrar por",
|
||||
arrayOf(
|
||||
Pair("Título", "title"),
|
||||
Pair("Autor", "author"),
|
||||
@ -346,36 +338,38 @@ class TuMangaOnline : ConfigurableSource, ParsedHttpSource() {
|
||||
Selection(0, false)
|
||||
)
|
||||
|
||||
private class WebcomicFilter : Filter.TriState("Webcomic")
|
||||
private class ContentType(name: String, val id: String) : Filter.TriState(name)
|
||||
|
||||
private class FourKomaFilter : Filter.TriState("Yonkoma")
|
||||
private class ContentTypeList(content: List<ContentType>) : Filter.Group<ContentType>("Filtrar por tipo de contenido", content)
|
||||
|
||||
private class AmateurFilter : Filter.TriState("Amateur")
|
||||
|
||||
private class EroticFilter : Filter.TriState("Erótico")
|
||||
|
||||
private class Genre(name: String, val id: String) : Filter.CheckBox(name)
|
||||
private class Genre(name: String, val id: String) : Filter.TriState(name)
|
||||
|
||||
private class GenreList(genres: List<Genre>) : Filter.Group<Genre>("Filtrar por géneros", genres)
|
||||
|
||||
override fun getFilterList() = FilterList(
|
||||
Types(),
|
||||
Filter.Separator(),
|
||||
Filter.Header("Ignorado sino se filtra por tipo"),
|
||||
Status(),
|
||||
Filter.Separator(),
|
||||
Filter.Header("Ignorado sino se filtra por tipo"),
|
||||
TranslationStatus(),
|
||||
Filter.Separator(),
|
||||
Demography(),
|
||||
Filter.Separator(),
|
||||
FilterBy(),
|
||||
Filter.Separator(),
|
||||
SortBy(),
|
||||
Filter.Separator(),
|
||||
WebcomicFilter(),
|
||||
FourKomaFilter(),
|
||||
AmateurFilter(),
|
||||
EroticFilter(),
|
||||
ContentTypeList(getContentTypeList()),
|
||||
Filter.Separator(),
|
||||
GenreList(getGenreList())
|
||||
)
|
||||
|
||||
// Array.from(document.querySelectorAll('#books-genders .col-auto .custom-control'))
|
||||
// .map(a => `Genre("${a.querySelector('label').innerText}", "${a.querySelector('input').value}")`).join(',\n')
|
||||
// on https://tumangaonline.me/library
|
||||
// Last revision 13/07/2020
|
||||
// on https://lectortmo.com/library
|
||||
// Last revision 15/02/2021
|
||||
private fun getGenreList() = listOf(
|
||||
Genre("Acción", "1"),
|
||||
Genre("Aventura", "2"),
|
||||
@ -427,94 +421,238 @@ class TuMangaOnline : ConfigurableSource, ParsedHttpSource() {
|
||||
Genre("Oeste", "48")
|
||||
)
|
||||
|
||||
private fun getContentTypeList() = listOf(
|
||||
ContentType("Webcomic", "webcomic"),
|
||||
ContentType("Yonkoma", "yonkoma"),
|
||||
ContentType("Amateur", "amateur"),
|
||||
ContentType("Erótico", "erotic")
|
||||
)
|
||||
|
||||
private open class UriPartFilter(displayName: String, val vals: Array<Pair<String, String>>) :
|
||||
Filter.Select<String>(displayName, vals.map { it.first }.toTypedArray()) {
|
||||
fun toUriPart() = vals[state].second
|
||||
}
|
||||
|
||||
private val preferences: SharedPreferences by lazy {
|
||||
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
|
||||
}
|
||||
|
||||
override fun setupPreferenceScreen(screen: androidx.preference.PreferenceScreen) {
|
||||
val deduppref = androidx.preference.ListPreference(screen.context).apply {
|
||||
key = DEDUP_PREF_Title
|
||||
title = DEDUP_PREF_Title
|
||||
entries = arrayOf("Mostrar todos los scanlators", "Mostrar solo un scanlator")
|
||||
entryValues = arrayOf("all", "one")
|
||||
summary = "%s"
|
||||
|
||||
val SFWModePref = androidx.preference.CheckBoxPreference(screen.context).apply {
|
||||
key = SFW_MODE_PREF
|
||||
title = SFW_MODE_PREF_TITLE
|
||||
summary = SFW_MODE_PREF_SUMMARY
|
||||
setDefaultValue(SFW_MODE_PREF_DEFAULT_VALUE)
|
||||
|
||||
setOnPreferenceChangeListener { _, newValue ->
|
||||
val selected = newValue as String
|
||||
val index = this.findIndexOfValue(selected)
|
||||
val entry = entryValues[index] as String
|
||||
preferences.edit().putString(DEDUP_PREF, entry).commit()
|
||||
val checkValue = newValue as Boolean
|
||||
preferences.edit().putBoolean(SFW_MODE_PREF, checkValue).commit()
|
||||
}
|
||||
}
|
||||
|
||||
val pageMethod = androidx.preference.ListPreference(screen.context).apply {
|
||||
key = PAGEGET_PREF_Title
|
||||
title = PAGEGET_PREF_Title
|
||||
entries = arrayOf("Cascada (recomendado)", "Paginado")
|
||||
val scanlatorPref = androidx.preference.CheckBoxPreference(screen.context).apply {
|
||||
key = SCANLATOR_PREF
|
||||
title = SCANLATOR_PREF_TITLE
|
||||
summary = SCANLATOR_PREF_SUMMARY
|
||||
setDefaultValue(SCANLATOR_PREF_DEFAULT_VALUE)
|
||||
|
||||
setOnPreferenceChangeListener { _, newValue ->
|
||||
val checkValue = newValue as Boolean
|
||||
preferences.edit().putBoolean(SCANLATOR_PREF, checkValue).commit()
|
||||
}
|
||||
}
|
||||
|
||||
val pageMethodPref = androidx.preference.ListPreference(screen.context).apply {
|
||||
key = PAGE_METHOD_PREF
|
||||
title = PAGE_METHOD_PREF_TITLE
|
||||
entries = arrayOf("Cascada", "Páginado")
|
||||
entryValues = arrayOf("cascade", "paginated")
|
||||
summary = "%s"
|
||||
summary = PAGE_METHOD_PREF_SUMMARY
|
||||
setDefaultValue(PAGE_METHOD_PREF_DEFAULT_VALUE)
|
||||
|
||||
setOnPreferenceChangeListener { _, newValue ->
|
||||
val selected = newValue as String
|
||||
val index = this.findIndexOfValue(selected)
|
||||
val entry = entryValues[index] as String
|
||||
preferences.edit().putString(PAGEGET_PREF, entry).commit()
|
||||
try {
|
||||
val setting = preferences.edit().putString(PAGE_METHOD_PREF, newValue as String).commit()
|
||||
setting
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
screen.addPreference(deduppref)
|
||||
screen.addPreference(pageMethod)
|
||||
// Rate limit
|
||||
val apiRateLimitPreference = androidx.preference.ListPreference(screen.context).apply {
|
||||
key = WEB_RATELIMIT_PREF
|
||||
title = WEB_RATELIMIT_PREF_TITLE
|
||||
summary = WEB_RATELIMIT_PREF_SUMMARY
|
||||
entries = ENTRIES_ARRAY
|
||||
entryValues = ENTRIES_ARRAY
|
||||
|
||||
setDefaultValue(WEB_RATELIMIT_PREF_DEFAULT_VALUE)
|
||||
setOnPreferenceChangeListener { _, newValue ->
|
||||
try {
|
||||
val setting = preferences.edit().putString(WEB_RATELIMIT_PREF, newValue as String).commit()
|
||||
setting
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val imgCDNRateLimitPreference = androidx.preference.ListPreference(screen.context).apply {
|
||||
key = IMAGE_CDN_RATELIMIT_PREF
|
||||
title = IMAGE_CDN_RATELIMIT_PREF_TITLE
|
||||
summary = IMAGE_CDN_RATELIMIT_PREF_SUMMARY
|
||||
entries = ENTRIES_ARRAY
|
||||
entryValues = ENTRIES_ARRAY
|
||||
|
||||
setDefaultValue(IMAGE_CDN_RATELIMIT_PREF_DEFAULT_VALUE)
|
||||
setOnPreferenceChangeListener { _, newValue ->
|
||||
try {
|
||||
val setting = preferences.edit().putString(IMAGE_CDN_RATELIMIT_PREF, newValue as String).commit()
|
||||
setting
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
screen.addPreference(SFWModePref)
|
||||
screen.addPreference(scanlatorPref)
|
||||
screen.addPreference(pageMethodPref)
|
||||
screen.addPreference(apiRateLimitPreference)
|
||||
screen.addPreference(imgCDNRateLimitPreference)
|
||||
}
|
||||
|
||||
override fun setupPreferenceScreen(screen: PreferenceScreen) {
|
||||
val deduppref = ListPreference(screen.context).apply {
|
||||
key = DEDUP_PREF_Title
|
||||
title = DEDUP_PREF_Title
|
||||
entries = arrayOf("Mostrar todos los scanlators", "Mostrar solo un scanlator")
|
||||
entryValues = arrayOf("all", "one")
|
||||
summary = "%s"
|
||||
|
||||
val SFWModePref = CheckBoxPreference(screen.context).apply {
|
||||
key = SFW_MODE_PREF
|
||||
title = SFW_MODE_PREF_TITLE
|
||||
summary = SFW_MODE_PREF_SUMMARY
|
||||
setDefaultValue(SFW_MODE_PREF_DEFAULT_VALUE)
|
||||
|
||||
setOnPreferenceChangeListener { _, newValue ->
|
||||
val selected = newValue as String
|
||||
val index = this.findIndexOfValue(selected)
|
||||
val entry = entryValues[index] as String
|
||||
preferences.edit().putString(DEDUP_PREF, entry).commit()
|
||||
val checkValue = newValue as Boolean
|
||||
preferences.edit().putBoolean(SFW_MODE_PREF, checkValue).commit()
|
||||
}
|
||||
}
|
||||
|
||||
val pageMethod = ListPreference(screen.context).apply {
|
||||
key = PAGEGET_PREF_Title
|
||||
title = PAGEGET_PREF_Title
|
||||
entries = arrayOf("Cascada (recomendado)", "Paginado")
|
||||
val scanlatorPref = CheckBoxPreference(screen.context).apply {
|
||||
key = SCANLATOR_PREF
|
||||
title = SCANLATOR_PREF_TITLE
|
||||
summary = SCANLATOR_PREF_SUMMARY
|
||||
setDefaultValue(SCANLATOR_PREF_DEFAULT_VALUE)
|
||||
|
||||
setOnPreferenceChangeListener { _, newValue ->
|
||||
val checkValue = newValue as Boolean
|
||||
preferences.edit().putBoolean(SCANLATOR_PREF, checkValue).commit()
|
||||
}
|
||||
}
|
||||
|
||||
val pageMethodPref = ListPreference(screen.context).apply {
|
||||
key = PAGE_METHOD_PREF
|
||||
title = PAGE_METHOD_PREF_TITLE
|
||||
entries = arrayOf("Cascada", "Páginado")
|
||||
entryValues = arrayOf("cascade", "paginated")
|
||||
summary = "%s"
|
||||
summary = PAGE_METHOD_PREF_SUMMARY
|
||||
setDefaultValue(PAGE_METHOD_PREF_DEFAULT_VALUE)
|
||||
|
||||
setOnPreferenceChangeListener { _, newValue ->
|
||||
val selected = newValue as String
|
||||
val index = this.findIndexOfValue(selected)
|
||||
val entry = entryValues[index] as String
|
||||
preferences.edit().putString(PAGEGET_PREF, entry).commit()
|
||||
try {
|
||||
val setting = preferences.edit().putString(PAGE_METHOD_PREF, newValue as String).commit()
|
||||
setting
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
screen.addPreference(deduppref)
|
||||
screen.addPreference(pageMethod)
|
||||
// Rate limit
|
||||
val apiRateLimitPreference = ListPreference(screen.context).apply {
|
||||
key = WEB_RATELIMIT_PREF
|
||||
title = WEB_RATELIMIT_PREF_TITLE
|
||||
summary = WEB_RATELIMIT_PREF_SUMMARY
|
||||
entries = ENTRIES_ARRAY
|
||||
entryValues = ENTRIES_ARRAY
|
||||
|
||||
setDefaultValue(WEB_RATELIMIT_PREF_DEFAULT_VALUE)
|
||||
setOnPreferenceChangeListener { _, newValue ->
|
||||
try {
|
||||
val setting = preferences.edit().putString(WEB_RATELIMIT_PREF, newValue as String).commit()
|
||||
setting
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val imgCDNRateLimitPreference = ListPreference(screen.context).apply {
|
||||
key = IMAGE_CDN_RATELIMIT_PREF
|
||||
title = IMAGE_CDN_RATELIMIT_PREF_TITLE
|
||||
summary = IMAGE_CDN_RATELIMIT_PREF_SUMMARY
|
||||
entries = ENTRIES_ARRAY
|
||||
entryValues = ENTRIES_ARRAY
|
||||
|
||||
setDefaultValue(IMAGE_CDN_RATELIMIT_PREF_DEFAULT_VALUE)
|
||||
setOnPreferenceChangeListener { _, newValue ->
|
||||
try {
|
||||
val setting = preferences.edit().putString(IMAGE_CDN_RATELIMIT_PREF, newValue as String).commit()
|
||||
setting
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
screen.addPreference(SFWModePref)
|
||||
screen.addPreference(scanlatorPref)
|
||||
screen.addPreference(pageMethodPref)
|
||||
screen.addPreference(apiRateLimitPreference)
|
||||
screen.addPreference(imgCDNRateLimitPreference)
|
||||
}
|
||||
|
||||
private fun getduppref() = preferences.getString(DEDUP_PREF, "all")
|
||||
private fun getScanlatorPref(): Boolean = preferences.getBoolean(SCANLATOR_PREF, SCANLATOR_PREF_DEFAULT_VALUE)
|
||||
|
||||
private fun getPageMethod() = preferences.getString(PAGEGET_PREF, "cascade")
|
||||
private fun getSFWModePref(): Boolean = preferences.getBoolean(SFW_MODE_PREF, SFW_MODE_PREF_DEFAULT_VALUE)
|
||||
|
||||
private fun getPageMethodPref() = preferences.getString(PAGE_METHOD_PREF, PAGE_METHOD_PREF_DEFAULT_VALUE)
|
||||
|
||||
companion object {
|
||||
private const val DEDUP_PREF_Title = "Preferencias de scanlator"
|
||||
private const val DEDUP_PREF = "deduppref"
|
||||
private const val PAGEGET_PREF_Title = "Método para la descarga de imágenes"
|
||||
private const val PAGEGET_PREF = "pagemethodpref"
|
||||
private const val SCANLATOR_PREF = "scanlatorPref"
|
||||
private const val SCANLATOR_PREF_TITLE = "Mostrar todos los scanlator"
|
||||
private const val SCANLATOR_PREF_SUMMARY = "Se mostraran capítulos repetidos pero con diferentes Scanlators"
|
||||
private const val SCANLATOR_PREF_DEFAULT_VALUE = true
|
||||
|
||||
private const val SFW_MODE_PREF = "SFWModePref"
|
||||
private const val SFW_MODE_PREF_TITLE = "Ocultar contenido NSFW"
|
||||
private const val SFW_MODE_PREF_SUMMARY = "Ocultar el contenido erótico (puede que aún activandolo se sigan mostrando portadas o series NSFW). Ten en cuenta que al activarlo se ignoran filtros al explorar y buscar.\nLos filtros ignorados son: Filtrar por tipo de contenido (Erotico) y el Filtrar por generos: Ecchi, Boys Love, Girls Love y Harem."
|
||||
private const val SFW_MODE_PREF_DEFAULT_VALUE = false
|
||||
private val SFW_MODE_PREF_EXCLUDE_GENDERS = listOf("6", "17", "18", "19")
|
||||
|
||||
private const val PAGE_METHOD_PREF = "pageMethodPref"
|
||||
private const val PAGE_METHOD_PREF_TITLE = "Método para descargar imágenes"
|
||||
private const val PAGE_METHOD_PREF_SUMMARY = "Previene ser banneado por el servidor cuando se usa la configuración \"Cascada\" ya que esta reduce la cantidad de solicitudes.\nPuedes usar \"Páginado\" cuando las imágenes no carguen usando \"Cascada\".\nConfiguración actual: %s"
|
||||
private const val PAGE_METHOD_PREF_DEFAULT_VALUE = "cascade"
|
||||
|
||||
private const val WEB_RATELIMIT_PREF = "webRatelimitPreference"
|
||||
// Ratelimit permits per second for main website
|
||||
private const val WEB_RATELIMIT_PREF_TITLE = "Ratelimit por minuto para el sitio web"
|
||||
// This value affects network request amount to TMO url. Lower this value may reduce the chance to get HTTP 429 error, but loading speed will be slower too. Tachiyomi restart required. \nCurrent value: %s
|
||||
private const val WEB_RATELIMIT_PREF_SUMMARY = "Este valor afecta la cantidad de solicitudes de red a la URL de TMO. Reducir este valor puede disminuir la posibilidad de obtener un error HTTP 429, pero la velocidad de descarga será más lenta. Se requiere reiniciar Tachiyomi. \nValor actual: %s"
|
||||
private const val WEB_RATELIMIT_PREF_DEFAULT_VALUE = "10"
|
||||
|
||||
private const val IMAGE_CDN_RATELIMIT_PREF = "imgCDNRatelimitPreference"
|
||||
// Ratelimit permits per second for image CDN
|
||||
private const val IMAGE_CDN_RATELIMIT_PREF_TITLE = "Ratelimit por minuto para descarga de imágenes"
|
||||
// This value affects network request amount for loading image. Lower this value may reduce the chance to get error when loading image, but loading speed will be slower too. Tachiyomi restart required. \nCurrent value: %s
|
||||
private const val IMAGE_CDN_RATELIMIT_PREF_SUMMARY = "Este valor afecta la cantidad de solicitudes de red para descargar imágenes. Reducir este valor puede disminuir errores al cargar imagenes, pero la velocidad de descarga será más lenta. Se requiere reiniciar Tachiyomi. \nValor actual: %s"
|
||||
private const val IMAGE_CDN_RATELIMIT_PREF_DEFAULT_VALUE = "10"
|
||||
|
||||
private val ENTRIES_ARRAY = (1..10).map { i -> i.toString() }.toTypedArray()
|
||||
|
||||
const val PREFIX_LIBRARY = "library"
|
||||
const val PREFIX_ID_SEARCH = "id:"
|
||||
|
Loading…
x
Reference in New Issue
Block a user