Bato.to added alternative chapter list and one more filter (#10393)

* Added alternate chapter list and one more filter

* Update build.gradle
This commit is contained in:
jopejoe1 2022-01-10 05:24:56 +13:00 committed by GitHub
parent 30cd5d47fb
commit 6146e0fd83
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 110 additions and 5 deletions

View File

@ -6,7 +6,7 @@ ext {
extName = 'Bato.to' extName = 'Bato.to'
pkgNameSuffix = 'all.batoto' pkgNameSuffix = 'all.batoto'
extClass = '.BatoToFactory' extClass = '.BatoToFactory'
extVersionCode = 18 extVersionCode = 19
isNsfw = true isNsfw = true
} }

View File

@ -2,10 +2,12 @@ package eu.kanade.tachiyomi.extension.all.batoto
import android.app.Application import android.app.Application
import android.content.SharedPreferences import android.content.SharedPreferences
import androidx.preference.CheckBoxPreference
import androidx.preference.ListPreference import androidx.preference.ListPreference
import androidx.preference.PreferenceScreen import androidx.preference.PreferenceScreen
import com.squareup.duktape.Duktape import com.squareup.duktape.Duktape
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.source.ConfigurableSource import eu.kanade.tachiyomi.source.ConfigurableSource
import eu.kanade.tachiyomi.source.model.Filter import eu.kanade.tachiyomi.source.model.Filter
@ -16,13 +18,18 @@ import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.ParsedHttpSource import eu.kanade.tachiyomi.source.online.ParsedHttpSource
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.jsonArray import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive import kotlinx.serialization.json.jsonPrimitive
import okhttp3.FormBody
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import okhttp3.Response import okhttp3.Response
import org.jsoup.Jsoup
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
import org.jsoup.parser.Parser import org.jsoup.parser.Parser
@ -30,7 +37,9 @@ import rx.Observable
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.text.SimpleDateFormat
import java.util.Calendar import java.util.Calendar
import java.util.Locale
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
open class BatoTo( open class BatoTo(
@ -43,7 +52,7 @@ open class BatoTo(
} }
override val name: String = "Bato.to" override val name: String = "Bato.to"
override val baseUrl: String = getResolutionPref()!! override val baseUrl: String = getMirrorPref()!!
override fun setupPreferenceScreen(screen: PreferenceScreen) { override fun setupPreferenceScreen(screen: PreferenceScreen) {
val mirrorPref = ListPreference(screen.context).apply { val mirrorPref = ListPreference(screen.context).apply {
@ -61,10 +70,23 @@ open class BatoTo(
preferences.edit().putString("${MIRROR_PREF_KEY}_$lang", entry).commit() preferences.edit().putString("${MIRROR_PREF_KEY}_$lang", entry).commit()
} }
} }
val altChapterListPref = CheckBoxPreference(screen.context).apply {
key = "${ALT_CHAPTER_LIST_PREF_KEY}_$lang"
title = ALT_CHAPTER_LIST_PREF_TITLE
summary = ALT_CHAPTER_LIST_PREF_SUMMARY
setDefaultValue(ALT_CHAPTER_LIST_PREF_DEFAULT_VALUE)
setOnPreferenceChangeListener { _, newValue ->
val checkValue = newValue as Boolean
preferences.edit().putBoolean("${ALT_CHAPTER_LIST_PREF_KEY}_$lang", checkValue).commit()
}
}
screen.addPreference(mirrorPref) screen.addPreference(mirrorPref)
screen.addPreference(altChapterListPref)
} }
private fun getResolutionPref(): String? = preferences.getString("${MIRROR_PREF_KEY}_$lang", MIRROR_PREF_DEFAULT_VALUE) private fun getMirrorPref(): String? = preferences.getString("${MIRROR_PREF_KEY}_$lang", MIRROR_PREF_DEFAULT_VALUE)
private fun getAltChapterListPref(): Boolean = preferences.getBoolean("${ALT_CHAPTER_LIST_PREF_KEY}_$lang", ALT_CHAPTER_LIST_PREF_DEFAULT_VALUE)
companion object { companion object {
private const val MIRROR_PREF_KEY = "MIRROR" private const val MIRROR_PREF_KEY = "MIRROR"
@ -72,6 +94,11 @@ open class BatoTo(
private val MIRROR_PREF_ENTRIES = arrayOf("Bato.to", "Batotoo.com", "Comiko.net", "Battwo.com", "Mangatoto.com", "Mycdhands.com") private val MIRROR_PREF_ENTRIES = arrayOf("Bato.to", "Batotoo.com", "Comiko.net", "Battwo.com", "Mangatoto.com", "Mycdhands.com")
private val MIRROR_PREF_ENTRY_VALUES = arrayOf("https://bato.to", "https://batotoo.com", "https://comiko.net", "https://battwo.com", "https://mangatoto.com", "https://mycdhands.com") private val MIRROR_PREF_ENTRY_VALUES = arrayOf("https://bato.to", "https://batotoo.com", "https://comiko.net", "https://battwo.com", "https://mangatoto.com", "https://mycdhands.com")
private val MIRROR_PREF_DEFAULT_VALUE = MIRROR_PREF_ENTRY_VALUES[0] private val MIRROR_PREF_DEFAULT_VALUE = MIRROR_PREF_ENTRY_VALUES[0]
private const val ALT_CHAPTER_LIST_PREF_KEY = "ALT_CHAPTER_LIST"
private const val ALT_CHAPTER_LIST_PREF_TITLE = "Alternative Chapter List"
private const val ALT_CHAPTER_LIST_PREF_SUMMARY = "If checked, uses an alternate chapter list"
private const val ALT_CHAPTER_LIST_PREF_DEFAULT_VALUE = false
} }
override val supportsLatest = true override val supportsLatest = true
@ -118,6 +145,7 @@ open class BatoTo(
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> { override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
val utilsFilter = filters.findInstance<UtilsFilter>()!! val utilsFilter = filters.findInstance<UtilsFilter>()!!
val letterFilter = filters.findInstance<LetterFilter>()!! val letterFilter = filters.findInstance<LetterFilter>()!!
val historyFilter = filters.findInstance<HistoryFilter>()!!
return when { return when {
query.startsWith("ID:") -> { query.startsWith("ID:") -> {
val id = query.substringAfter("ID:") val id = query.substringAfter("ID:")
@ -147,6 +175,13 @@ open class BatoTo(
queryUtilsParse(response) queryUtilsParse(response)
} }
} }
historyFilter.state != 0 -> {
val url = "$baseUrl/ajax.my.${historyFilter.selected}.paging"
client.newCall(POST(url, headers, formBuilder().build())).asObservableSuccess()
.map { response ->
queryHistoryParse(response)
}
}
else -> { else -> {
val sortFilter = filters.findInstance<SortFilter>()!! val sortFilter = filters.findInstance<SortFilter>()!!
val reverseSortFilter = filters.findInstance<ReverseSortFilter>()!! val reverseSortFilter = filters.findInstance<ReverseSortFilter>()!!
@ -228,14 +263,39 @@ open class BatoTo(
return MangasPage(mangas, false) return MangasPage(mangas, false)
} }
private fun queryHistoryParse(response: Response): MangasPage {
val json = json.decodeFromString<JsonObject>(response.body!!.string())
val html = json.jsonObject["html"]!!.jsonPrimitive.content
val document = Jsoup.parse(html, response.request.url.toString())
val mangas = document.select(".my-history-item")
.map { element -> searchHistoryFromElement(element) }
return MangasPage(mangas, false)
}
private fun searchUtilsFromElement(element: Element): SManga { private fun searchUtilsFromElement(element: Element): SManga {
val manga = SManga.create() val manga = SManga.create()
manga.setUrlWithoutDomain(element.select("td a").attr("href")) manga.setUrlWithoutDomain(element.select("td a").attr("href"))
manga.title = element.select("td a").text().removeEntities() manga.title = element.select("td a").text()
manga.thumbnail_url = element.select("img").attr("abs:src") manga.thumbnail_url = element.select("img").attr("abs:src")
return manga return manga
} }
private fun searchHistoryFromElement(element: Element): SManga {
val manga = SManga.create()
manga.setUrlWithoutDomain(element.select(".position-relative a").attr("href"))
manga.title = element.select(".position-relative a").text()
manga.thumbnail_url = element.select("img").attr("abs:src")
return manga
}
open fun formBuilder() = FormBody.Builder().apply {
add("_where", "browse")
add("first", "0")
add("limit", "0")
add("prevPos", "null")
}
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request = throw UnsupportedOperationException("Not used") override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request = throw UnsupportedOperationException("Not used")
override fun searchMangaSelector() = throw UnsupportedOperationException("Not used") override fun searchMangaSelector() = throw UnsupportedOperationException("Not used")
override fun searchMangaFromElement(element: Element) = throw UnsupportedOperationException("Not used") override fun searchMangaFromElement(element: Element) = throw UnsupportedOperationException("Not used")
@ -257,7 +317,7 @@ open class BatoTo(
manga.artist = infoElement.select("div.attr-item:contains(author) a:last-child").text() manga.artist = infoElement.select("div.attr-item:contains(author) a:last-child").text()
manga.status = parseStatus(status) manga.status = parseStatus(status)
manga.genre = infoElement.select(".attr-item b:contains(genres) + span ").joinToString { it.text() } manga.genre = infoElement.select(".attr-item b:contains(genres) + span ").joinToString { it.text() }
manga.description = infoElement.select("div.limit-html").text() + "\n\n" + infoElement.select(".episode-list > .alert-warning").text() manga.description = infoElement.select("div.limit-html").text() + "\n" + infoElement.select(".episode-list > .alert-warning").text().trim()
manga.thumbnail_url = document.select("div.attr-cover img") manga.thumbnail_url = document.select("div.attr-cover img")
.attr("abs:src") .attr("abs:src")
return manga return manga
@ -270,6 +330,39 @@ open class BatoTo(
else -> SManga.UNKNOWN else -> SManga.UNKNOWN
} }
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> {
val url = client.newCall(
GET(
when {
manga.url.startsWith("http") -> manga.url
else -> "$baseUrl${manga.url}"
}
)
).execute().asJsoup()
if (getAltChapterListPref() || checkChapterLists(url)) {
val id = manga.url.substringBeforeLast("/").substringAfterLast("/").trim()
return client.newCall(GET("$baseUrl/rss/series/$id.xml"))
.asObservableSuccess()
.map { altChapterParse(it, manga.title) }
}
return super.fetchChapterList(manga)
}
private fun altChapterParse(response: Response, title: String): List<SChapter> {
return Jsoup.parse(response.body!!.string(), response.request.url.toString(), Parser.xmlParser())
.select("channel > item").map { item ->
SChapter.create().apply {
url = item.selectFirst("guid").text()
name = item.selectFirst("title").text().substringAfter(title).trim()
date_upload = SimpleDateFormat("E, dd MMM yyyy H:m:s Z", Locale.US).parse(item.selectFirst("pubDate").text())?.time ?: 0L
}
}
}
private fun checkChapterLists(document: Document): Boolean {
return document.select(".episode-list > .alert-warning").text().contains("This comic has been marked as deleted and the chapter list is not available.")
}
override fun chapterListRequest(manga: SManga): Request { override fun chapterListRequest(manga: SManga): Request {
if (manga.url.startsWith("http")) { if (manga.url.startsWith("http")) {
return GET(manga.url, headers) return GET(manga.url, headers)
@ -404,6 +497,7 @@ open class BatoTo(
override fun getFilterList() = FilterList( override fun getFilterList() = FilterList(
LetterFilter(getLetterFilter(), 0), LetterFilter(getLetterFilter(), 0),
Filter.Separator(),
Filter.Header("NOTE: Ignored if using text search!"), Filter.Header("NOTE: Ignored if using text search!"),
Filter.Separator(), Filter.Separator(),
SortFilter(getSortFilter(), 5), SortFilter(getSortFilter(), 5),
@ -416,7 +510,10 @@ open class BatoTo(
ReverseSortFilter(), ReverseSortFilter(),
Filter.Separator(), Filter.Separator(),
Filter.Header("NOTE: Filters below are incompatible with any other filters!"), Filter.Header("NOTE: Filters below are incompatible with any other filters!"),
Filter.Header("NOTE: Login Required!"),
Filter.Separator(),
UtilsFilter(getUtilsFilter(), 0), UtilsFilter(getUtilsFilter(), 0),
HistoryFilter(getHistoryFilter(), 0),
) )
class SelectFilterOption(val name: String, val value: String) class SelectFilterOption(val name: String, val value: String)
@ -453,11 +550,13 @@ open class BatoTo(
class LangGroupFilter(options: List<CheckboxFilterOption>) : CheckboxGroupFilter("Languages", options) class LangGroupFilter(options: List<CheckboxFilterOption>) : CheckboxGroupFilter("Languages", options)
class LetterFilter(options: List<SelectFilterOption>, default: Int) : SelectFilter("Letter matching mode (Slow)", options, default) class LetterFilter(options: List<SelectFilterOption>, default: Int) : SelectFilter("Letter matching mode (Slow)", options, default)
class UtilsFilter(options: List<SelectFilterOption>, default: Int) : SelectFilter("Utils comic list", options, default) class UtilsFilter(options: List<SelectFilterOption>, default: Int) : SelectFilter("Utils comic list", options, default)
class HistoryFilter(options: List<SelectFilterOption>, default: Int) : SelectFilter("Personal list", options, default)
private fun getLetterFilter() = listOf( private fun getLetterFilter() = listOf(
SelectFilterOption("Disabled", "disabled"), SelectFilterOption("Disabled", "disabled"),
SelectFilterOption("Enabled", "enabled"), SelectFilterOption("Enabled", "enabled"),
) )
private fun getSortFilter() = listOf( private fun getSortFilter() = listOf(
SelectFilterOption("Z-A", "title"), SelectFilterOption("Z-A", "title"),
SelectFilterOption("Last Updated", "update"), SelectFilterOption("Last Updated", "update"),
@ -470,6 +569,12 @@ open class BatoTo(
SelectFilterOption("Most Views 60 minutes", "views_h"), SelectFilterOption("Most Views 60 minutes", "views_h"),
) )
private fun getHistoryFilter() = listOf(
SelectFilterOption("None", ""),
SelectFilterOption("My History", "history"),
SelectFilterOption("My Updates", "updates"),
)
private fun getUtilsFilter() = listOf( private fun getUtilsFilter() = listOf(
SelectFilterOption("None", ""), SelectFilterOption("None", ""),
SelectFilterOption("Comics: I Created", "i-created"), SelectFilterOption("Comics: I Created", "i-created"),