Yeet old MangaPark (#16215)
This commit is contained in:
parent
53dafc1a1b
commit
b1fc79fe9f
|
@ -1,2 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest package="eu.kanade.tachiyomi.extension" />
|
|
@ -1,20 +0,0 @@
|
|||
# MangaPark
|
||||
|
||||
Table of Content
|
||||
- [FAQ](#FAQ)
|
||||
|
||||
[Uncomment this if needed; and replace ( and ) with ( and )]: <> (- [Guides](#Guides))
|
||||
|
||||
Don't find the question you are look for go check out our general FAQs and Guides over at [Extension FAQ](https://tachiyomi.org/help/faq/#extensions) or [Getting Started](https://tachiyomi.org/help/guides/getting-started/#installation)
|
||||
|
||||
## FAQ
|
||||
|
||||
### How do I deal with duplicate chapters?
|
||||
To solve this issue, follow the below steps.
|
||||
|
||||
1. Go to **Browse** → **Extensions**.
|
||||
1. Click on **MangaPark** extension and then **Chapter List Source**.
|
||||
1. Choose an option like **Smart list** or **Prioritize source**.
|
||||
1. Go back to **MangaPark**'s chapter list and refresh it.
|
||||
|
||||
[Uncomment this if needed]: <> (## Guides)
|
|
@ -1,12 +0,0 @@
|
|||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlinx-serialization'
|
||||
|
||||
ext {
|
||||
extName = 'MangaPark'
|
||||
pkgNameSuffix = 'en.mangapark'
|
||||
extClass = '.MangaPark'
|
||||
extVersionCode = 23
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
Binary file not shown.
Before Width: | Height: | Size: 4.2 KiB |
Binary file not shown.
Before Width: | Height: | Size: 2.2 KiB |
Binary file not shown.
Before Width: | Height: | Size: 6.0 KiB |
Binary file not shown.
Before Width: | Height: | Size: 12 KiB |
Binary file not shown.
Before Width: | Height: | Size: 17 KiB |
Binary file not shown.
Before Width: | Height: | Size: 87 KiB |
|
@ -1,614 +0,0 @@
|
|||
package eu.kanade.tachiyomi.extension.en.mangapark
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Application
|
||||
import android.content.SharedPreferences
|
||||
import android.net.Uri
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
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.Page
|
||||
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 kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.jsonArray
|
||||
import kotlinx.serialization.json.jsonObject
|
||||
import kotlinx.serialization.json.jsonPrimitive
|
||||
import okhttp3.CacheControl
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import org.jsoup.nodes.Document
|
||||
import org.jsoup.nodes.Element
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Calendar
|
||||
import java.util.Locale
|
||||
|
||||
class MangaPark : ConfigurableSource, ParsedHttpSource() {
|
||||
|
||||
override val lang = "en"
|
||||
|
||||
override val client = network.cloudflareClient
|
||||
|
||||
override val supportsLatest = true
|
||||
override val name = "MangaPark"
|
||||
override val baseUrl = "https://v2.mangapark.net"
|
||||
|
||||
private val nextPageSelector = ".paging:not(.order) > li:last-child > a"
|
||||
private val json: Json by injectLazy()
|
||||
private val dateFormat = SimpleDateFormat("MMM d, yyyy, HH:mm a", Locale.ENGLISH)
|
||||
private val dateFormatTimeOnly = SimpleDateFormat("HH:mm a", Locale.ENGLISH)
|
||||
|
||||
override fun popularMangaRequest(page: Int) = GET("$baseUrl/search?orderby=views_a&page=$page")
|
||||
|
||||
override fun popularMangaSelector() = searchMangaSelector()
|
||||
|
||||
override fun popularMangaFromElement(element: Element) = mangaFromElement(element)
|
||||
|
||||
override fun popularMangaNextPageSelector() = nextPageSelector
|
||||
|
||||
override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/latest" + if (page > 1) "/$page" else "")
|
||||
|
||||
override fun latestUpdatesSelector() = ".ls1 .item"
|
||||
|
||||
override fun latestUpdatesFromElement(element: Element) = mangaFromElement(element)
|
||||
|
||||
override fun latestUpdatesNextPageSelector() = nextPageSelector
|
||||
|
||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
||||
val uri = Uri.parse("$baseUrl/search").buildUpon()
|
||||
if (query.isNotEmpty()) {
|
||||
uri.appendQueryParameter("q", query)
|
||||
}
|
||||
filters.forEach {
|
||||
if (it is UriFilter) {
|
||||
it.addToUri(uri)
|
||||
}
|
||||
}
|
||||
if (page != 1) {
|
||||
uri.appendQueryParameter("page", page.toString())
|
||||
}
|
||||
return GET(uri.toString())
|
||||
}
|
||||
|
||||
override fun searchMangaSelector() = ".item"
|
||||
|
||||
override fun searchMangaFromElement(element: Element) = mangaFromElement(element)
|
||||
|
||||
override fun searchMangaNextPageSelector() = nextPageSelector
|
||||
|
||||
private fun mangaFromElement(element: Element) = SManga.create().apply {
|
||||
val coverElement = element.getElementsByClass("cover").first()!!
|
||||
url = coverElement.attr("href")
|
||||
title = coverElement.attr("title")
|
||||
thumbnail_url = coverElement.select("img").attr("abs:src")
|
||||
}
|
||||
|
||||
@SuppressLint("DefaultLocale")
|
||||
override fun mangaDetailsParse(document: Document) = SManga.create().apply {
|
||||
document.select(".cover > img").first()!!.let { coverElement ->
|
||||
title = coverElement.attr("title")
|
||||
thumbnail_url = coverElement.attr("abs:src")
|
||||
}
|
||||
|
||||
document.select(".attr > tbody > tr").forEach {
|
||||
when (it.getElementsByTag("th").first()!!.text().trim().lowercase()) {
|
||||
"author(s)" -> {
|
||||
author = it.getElementsByTag("a").joinToString(transform = Element::text)
|
||||
}
|
||||
"artist(s)" -> {
|
||||
artist = it.getElementsByTag("a").joinToString(transform = Element::text)
|
||||
}
|
||||
"genre(s)" -> {
|
||||
genre = it.getElementsByTag("a").joinToString(transform = Element::text)
|
||||
}
|
||||
"status" -> {
|
||||
status = when (it.getElementsByTag("td").text().trim().lowercase()) {
|
||||
"ongoing" -> SManga.ONGOING
|
||||
"completed" -> SManga.COMPLETED
|
||||
else -> SManga.UNKNOWN
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
description = document.getElementsByClass("summary").text().trim()
|
||||
|
||||
// add alternative name to manga description
|
||||
val altName = "Alternative Name: "
|
||||
document.select(".attr > tbody > tr:contains(Alter) td").firstOrNull()?.ownText()?.let {
|
||||
if (it.isBlank().not()) {
|
||||
description = when {
|
||||
description.isNullOrBlank() -> altName + it
|
||||
else -> description + "\n\n$altName" + it
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// force network to make sure chapter prefs take effect
|
||||
override fun chapterListRequest(manga: SManga): Request {
|
||||
return GET(baseUrl + manga.url, headers, CacheControl.FORCE_NETWORK)
|
||||
}
|
||||
|
||||
override fun chapterListParse(response: Response): List<SChapter> {
|
||||
fun List<SChapter>.getMissingChapters(allChapters: List<SChapter>): List<SChapter> {
|
||||
val chapterNums = this.map { it.chapter_number }
|
||||
return allChapters.filter { it.chapter_number !in chapterNums }.distinctBy { it.chapter_number }
|
||||
}
|
||||
|
||||
fun List<SChapter>.filterOrAll(source: String): List<SChapter> {
|
||||
val chapters = this.filter { it.scanlator!!.contains(source) }
|
||||
return if (chapters.isNotEmpty()) {
|
||||
(chapters + chapters.getMissingChapters(this)).sortedByDescending { it.chapter_number }
|
||||
} else {
|
||||
this
|
||||
}
|
||||
}
|
||||
|
||||
val mangaBySource = response.asJsoup().select("div[id^=stream]")
|
||||
.map { sourceElement ->
|
||||
var lastNum = 0F
|
||||
val sourceName = sourceElement.select("i + span").text()
|
||||
|
||||
sourceElement.select(chapterListSelector())
|
||||
.reversed() // so incrementing lastNum works
|
||||
.map { chapterElement ->
|
||||
chapterFromElement(chapterElement, sourceName, lastNum)
|
||||
.also { lastNum = it.chapter_number }
|
||||
}
|
||||
.distinctBy { it.chapter_number } // there's even duplicate chapters within a source ( -.- )
|
||||
}
|
||||
|
||||
return when (getSourcePref()) {
|
||||
// source with most chapters along with chapters that source doesn't have
|
||||
"most" -> {
|
||||
val chapters = mangaBySource.maxByOrNull { it.count() }!!
|
||||
(chapters + chapters.getMissingChapters(mangaBySource.flatten())).sortedByDescending { it.chapter_number }
|
||||
}
|
||||
// "smart list" - try not to miss a chapter and avoid dupes
|
||||
"smart" -> mangaBySource.flatten().distinctBy { it.chapter_number }.sortedByDescending { it.chapter_number }
|
||||
// use a specific source + any missing chapters, display all if none available from that source
|
||||
"rock" -> mangaBySource.flatten().filterOrAll("Rock")
|
||||
"duck" -> mangaBySource.flatten().filterOrAll("Duck")
|
||||
"mini" -> mangaBySource.flatten().filterOrAll("Mini")
|
||||
"fox" -> mangaBySource.flatten().filterOrAll("Fox")
|
||||
"panda" -> mangaBySource.flatten().filterOrAll("Panda")
|
||||
// all sources, all chapters
|
||||
else -> mangaBySource.flatMap { it.reversed() }
|
||||
}
|
||||
}
|
||||
|
||||
override fun chapterListSelector() = ".volume .chapter li"
|
||||
|
||||
private val chapterNumberRegex = Regex("""\b\d+\.?\d?\b""")
|
||||
|
||||
private fun chapterFromElement(element: Element, source: String, lastNum: Float): SChapter {
|
||||
fun Float.incremented() = this + .00001F
|
||||
fun Float?.orIncrementLastNum() = if (this == null || this < lastNum) lastNum.incremented() else this
|
||||
|
||||
return SChapter.create().apply {
|
||||
element.select(".tit > a").first()!!.let {
|
||||
url = it.attr("href").removeSuffix("1")
|
||||
name = it.text()
|
||||
}
|
||||
// Get the chapter number or create a unique one if it's not available
|
||||
chapter_number = chapterNumberRegex.findAll(name)
|
||||
.toList()
|
||||
.map { it.value.toFloatOrNull() }
|
||||
.let { nums ->
|
||||
when {
|
||||
nums.count() == 1 -> nums[0].orIncrementLastNum()
|
||||
nums.count() >= 2 -> nums[1].orIncrementLastNum()
|
||||
else -> lastNum.incremented()
|
||||
}
|
||||
}
|
||||
date_upload = parseDate(element.select(".time").first()!!.text().trim())
|
||||
scanlator = source
|
||||
}
|
||||
}
|
||||
|
||||
override fun chapterFromElement(element: Element): SChapter = throw UnsupportedOperationException("Not used")
|
||||
|
||||
@SuppressLint("DefaultLocale")
|
||||
private fun parseDate(date: String): Long {
|
||||
val lcDate = date.lowercase()
|
||||
if (lcDate.endsWith("ago")) return parseRelativeDate(lcDate)
|
||||
|
||||
// Handle 'yesterday' and 'today'
|
||||
var relativeDate: Calendar? = null
|
||||
if (lcDate.startsWith("yesterday")) {
|
||||
relativeDate = Calendar.getInstance()
|
||||
relativeDate.add(Calendar.DAY_OF_MONTH, -1) // yesterday
|
||||
} else if (lcDate.startsWith("today")) {
|
||||
relativeDate = Calendar.getInstance()
|
||||
}
|
||||
|
||||
relativeDate?.let {
|
||||
// Since the date is not specified, it defaults to 1970!
|
||||
val time = dateFormatTimeOnly.parse(lcDate.substringAfter(' ')) ?: return 0
|
||||
val cal = Calendar.getInstance()
|
||||
cal.time = time
|
||||
|
||||
// Copy time to relative date
|
||||
it.set(Calendar.HOUR_OF_DAY, cal.get(Calendar.HOUR_OF_DAY))
|
||||
it.set(Calendar.MINUTE, cal.get(Calendar.MINUTE))
|
||||
return it.timeInMillis
|
||||
}
|
||||
|
||||
return dateFormat.parse(lcDate)?.time ?: 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses dates in this form:
|
||||
* `11 days ago`
|
||||
*/
|
||||
private fun parseRelativeDate(date: String): Long {
|
||||
val trimmedDate = date.split(" ")
|
||||
|
||||
if (trimmedDate[2] != "ago") return 0
|
||||
|
||||
val number = when (trimmedDate[0]) {
|
||||
"a" -> 1
|
||||
else -> trimmedDate[0].toIntOrNull() ?: return 0
|
||||
}
|
||||
val unit = trimmedDate[1].removeSuffix("s") // Remove 's' suffix
|
||||
|
||||
val now = Calendar.getInstance()
|
||||
|
||||
// Map English unit to Java unit
|
||||
val javaUnit = when (unit) {
|
||||
"year" -> Calendar.YEAR
|
||||
"month" -> Calendar.MONTH
|
||||
"week" -> Calendar.WEEK_OF_MONTH
|
||||
"day" -> Calendar.DAY_OF_MONTH
|
||||
"hour" -> Calendar.HOUR
|
||||
"minute" -> Calendar.MINUTE
|
||||
"second" -> Calendar.SECOND
|
||||
else -> return 0
|
||||
}
|
||||
|
||||
now.add(javaUnit, -number)
|
||||
|
||||
return now.timeInMillis
|
||||
}
|
||||
|
||||
private val objRegex = Regex("""var _load_pages = (\[.*])""")
|
||||
|
||||
override fun pageListParse(response: Response): List<Page> {
|
||||
val obj = objRegex.find(response.body.string())?.groupValues?.get(1)
|
||||
?: throw Exception("_load_pages not found - ${response.request.url}")
|
||||
|
||||
return json.parseToJsonElement(obj).jsonArray.mapIndexed { i, it ->
|
||||
val url = it.jsonObject["u"]!!.jsonPrimitive.content
|
||||
Page(i, imageUrl = if (url.startsWith("//")) "https://$url" else url)
|
||||
}
|
||||
}
|
||||
|
||||
override fun pageListParse(document: Document): List<Page> = throw UnsupportedOperationException("Not used")
|
||||
|
||||
// Unused, we can get image urls directly from the chapter page
|
||||
override fun imageUrlParse(document: Document) = throw UnsupportedOperationException("Not used")
|
||||
|
||||
override fun getFilterList() = FilterList(
|
||||
AuthorArtistText(),
|
||||
SearchTypeFilter("Title query", "name-match"),
|
||||
SearchTypeFilter("Author/Artist query", "autart-match"),
|
||||
SortFilter(),
|
||||
GenreGroup(),
|
||||
GenreInclusionFilter(),
|
||||
ChapterCountFilter(),
|
||||
StatusFilter(),
|
||||
RatingFilter(),
|
||||
TypeFilter(),
|
||||
YearFilter(),
|
||||
)
|
||||
|
||||
private class SearchTypeFilter(name: String, val uriParam: String) :
|
||||
Filter.Select<String>(name, STATE_MAP), UriFilter {
|
||||
override fun addToUri(uri: Uri.Builder) {
|
||||
if (STATE_MAP[state] != "contain") {
|
||||
uri.appendQueryParameter(uriParam, STATE_MAP[state])
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val STATE_MAP = arrayOf("contain", "begin", "end")
|
||||
}
|
||||
}
|
||||
|
||||
private class AuthorArtistText : Filter.Text("Author/Artist"), UriFilter {
|
||||
override fun addToUri(uri: Uri.Builder) {
|
||||
if (state.isNotEmpty()) {
|
||||
uri.appendQueryParameter("autart", state)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class GenreFilter(val uriParam: String, displayName: String) : Filter.TriState(displayName)
|
||||
|
||||
private class GenreGroup :
|
||||
Filter.Group<GenreFilter>(
|
||||
"Genres",
|
||||
listOf(
|
||||
GenreFilter("4-koma", "4 koma"),
|
||||
GenreFilter("action", "Action"),
|
||||
GenreFilter("adaptation", "Adaptation"),
|
||||
GenreFilter("adult", "Adult"),
|
||||
GenreFilter("adventure", "Adventure"),
|
||||
GenreFilter("aliens", "Aliens"),
|
||||
GenreFilter("animals", "Animals"),
|
||||
GenreFilter("anthology", "Anthology"),
|
||||
GenreFilter("award-winning", "Award winning"),
|
||||
GenreFilter("comedy", "Comedy"),
|
||||
GenreFilter("cooking", "Cooking"),
|
||||
GenreFilter("crime", "Crime"),
|
||||
GenreFilter("crossdressing", "Crossdressing"),
|
||||
GenreFilter("delinquents", "Delinquents"),
|
||||
GenreFilter("demons", "Demons"),
|
||||
GenreFilter("doujinshi", "Doujinshi"),
|
||||
GenreFilter("drama", "Drama"),
|
||||
GenreFilter("ecchi", "Ecchi"),
|
||||
GenreFilter("fan-colored", "Fan colored"),
|
||||
GenreFilter("fantasy", "Fantasy"),
|
||||
GenreFilter("food", "Food"),
|
||||
GenreFilter("full-color", "Full color"),
|
||||
GenreFilter("game", "Game"),
|
||||
GenreFilter("gender-bender", "Gender bender"),
|
||||
GenreFilter("genderswap", "Genderswap"),
|
||||
GenreFilter("ghosts", "Ghosts"),
|
||||
GenreFilter("gore", "Gore"),
|
||||
GenreFilter("gossip", "Gossip"),
|
||||
GenreFilter("gyaru", "Gyaru"),
|
||||
GenreFilter("harem", "Harem"),
|
||||
GenreFilter("historical", "Historical"),
|
||||
GenreFilter("horror", "Horror"),
|
||||
GenreFilter("incest", "Incest"),
|
||||
GenreFilter("isekai", "Isekai"),
|
||||
GenreFilter("josei", "Josei"),
|
||||
GenreFilter("kids", "Kids"),
|
||||
GenreFilter("loli", "Loli"),
|
||||
GenreFilter("lolicon", "Lolicon"),
|
||||
GenreFilter("long-strip", "Long strip"),
|
||||
GenreFilter("mafia", "Mafia"),
|
||||
GenreFilter("magic", "Magic"),
|
||||
GenreFilter("magical-girls", "Magical girls"),
|
||||
GenreFilter("manhwa", "Manhwa"),
|
||||
GenreFilter("martial-arts", "Martial arts"),
|
||||
GenreFilter("mature", "Mature"),
|
||||
GenreFilter("mecha", "Mecha"),
|
||||
GenreFilter("medical", "Medical"),
|
||||
GenreFilter("military", "Military"),
|
||||
GenreFilter("monster-girls", "Monster girls"),
|
||||
GenreFilter("monsters", "Monsters"),
|
||||
GenreFilter("music", "Music"),
|
||||
GenreFilter("mystery", "Mystery"),
|
||||
GenreFilter("ninja", "Ninja"),
|
||||
GenreFilter("office-workers", "Office workers"),
|
||||
GenreFilter("official-colored", "Official colored"),
|
||||
GenreFilter("one-shot", "One shot"),
|
||||
GenreFilter("parody", "Parody"),
|
||||
GenreFilter("philosophical", "Philosophical"),
|
||||
GenreFilter("police", "Police"),
|
||||
GenreFilter("post-apocalyptic", "Post apocalyptic"),
|
||||
GenreFilter("psychological", "Psychological"),
|
||||
GenreFilter("reincarnation", "Reincarnation"),
|
||||
GenreFilter("reverse-harem", "Reverse harem"),
|
||||
GenreFilter("romance", "Romance"),
|
||||
GenreFilter("samurai", "Samurai"),
|
||||
GenreFilter("school-life", "School life"),
|
||||
GenreFilter("sci-fi", "Sci fi"),
|
||||
GenreFilter("seinen", "Seinen"),
|
||||
GenreFilter("shota", "Shota"),
|
||||
GenreFilter("shotacon", "Shotacon"),
|
||||
GenreFilter("shoujo", "Shoujo"),
|
||||
GenreFilter("shoujo-ai", "Shoujo ai"),
|
||||
GenreFilter("shounen", "Shounen"),
|
||||
GenreFilter("shounen-ai", "Shounen ai"),
|
||||
GenreFilter("slice-of-life", "Slice of life"),
|
||||
GenreFilter("smut", "Smut"),
|
||||
GenreFilter("space", "Space"),
|
||||
GenreFilter("sports", "Sports"),
|
||||
GenreFilter("super-power", "Super power"),
|
||||
GenreFilter("superhero", "Superhero"),
|
||||
GenreFilter("supernatural", "Supernatural"),
|
||||
GenreFilter("survival", "Survival"),
|
||||
GenreFilter("suspense", "Suspense"),
|
||||
GenreFilter("thriller", "Thriller"),
|
||||
GenreFilter("time-travel", "Time travel"),
|
||||
GenreFilter("toomics", "Toomics"),
|
||||
GenreFilter("traditional-games", "Traditional games"),
|
||||
GenreFilter("tragedy", "Tragedy"),
|
||||
GenreFilter("user-created", "User created"),
|
||||
GenreFilter("vampire", "Vampire"),
|
||||
GenreFilter("vampires", "Vampires"),
|
||||
GenreFilter("video-games", "Video games"),
|
||||
GenreFilter("virtual-reality", "Virtual reality"),
|
||||
GenreFilter("web-comic", "Web comic"),
|
||||
GenreFilter("webtoon", "Webtoon"),
|
||||
GenreFilter("wuxia", "Wuxia"),
|
||||
GenreFilter("yaoi", "Yaoi"),
|
||||
GenreFilter("yuri", "Yuri"),
|
||||
GenreFilter("zombies", "Zombies"),
|
||||
),
|
||||
),
|
||||
UriFilter {
|
||||
override fun addToUri(uri: Uri.Builder) {
|
||||
val genresParameterValue = state.filter { it.isIncluded() }.joinToString(",") { it.uriParam }
|
||||
if (genresParameterValue.isNotEmpty()) {
|
||||
uri.appendQueryParameter("genres", genresParameterValue)
|
||||
}
|
||||
|
||||
val genresExcludeParameterValue = state.filter { it.isExcluded() }.joinToString(",") { it.uriParam }
|
||||
if (genresExcludeParameterValue.isNotEmpty()) {
|
||||
uri.appendQueryParameter("genres-exclude", genresExcludeParameterValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class GenreInclusionFilter : UriSelectFilter(
|
||||
"Genre inclusion",
|
||||
"genres-mode",
|
||||
arrayOf(
|
||||
Pair("and", "And mode"),
|
||||
Pair("or", "Or mode"),
|
||||
),
|
||||
)
|
||||
|
||||
private class ChapterCountFilter : UriSelectFilter(
|
||||
"Chapter count",
|
||||
"chapters",
|
||||
arrayOf(
|
||||
Pair("any", "Any"),
|
||||
Pair("1", "1 +"),
|
||||
Pair("5", "5 +"),
|
||||
Pair("10", "10 +"),
|
||||
Pair("20", "20 +"),
|
||||
Pair("30", "30 +"),
|
||||
Pair("40", "40 +"),
|
||||
Pair("50", "50 +"),
|
||||
Pair("100", "100 +"),
|
||||
Pair("150", "150 +"),
|
||||
Pair("200", "200 +"),
|
||||
),
|
||||
)
|
||||
|
||||
private class StatusFilter : UriSelectFilter(
|
||||
"Status",
|
||||
"status",
|
||||
arrayOf(
|
||||
Pair("any", "Any"),
|
||||
Pair("completed", "Completed"),
|
||||
Pair("ongoing", "Ongoing"),
|
||||
),
|
||||
)
|
||||
|
||||
private class RatingFilter : UriSelectFilter(
|
||||
"Rating",
|
||||
"rating",
|
||||
arrayOf(
|
||||
Pair("any", "Any"),
|
||||
Pair("5", "5 stars"),
|
||||
Pair("4", "4 stars"),
|
||||
Pair("3", "3 stars"),
|
||||
Pair("2", "2 stars"),
|
||||
Pair("1", "1 star"),
|
||||
Pair("0", "0 stars"),
|
||||
),
|
||||
)
|
||||
|
||||
private class TypeFilter : UriSelectFilter(
|
||||
"Type",
|
||||
"types",
|
||||
arrayOf(
|
||||
Pair("any", "Any"),
|
||||
Pair("manga", "Japanese Manga"),
|
||||
Pair("manhwa", "Korean Manhwa"),
|
||||
Pair("manhua", "Chinese Manhua"),
|
||||
Pair("unknown", "Unknown"),
|
||||
),
|
||||
)
|
||||
|
||||
private class YearFilter : UriSelectFilter(
|
||||
"Release year",
|
||||
"years",
|
||||
arrayOf(
|
||||
Pair("any", "Any"),
|
||||
// Get all years between today and 1946
|
||||
*(Calendar.getInstance().get(Calendar.YEAR) downTo 1946).map {
|
||||
Pair(it.toString(), it.toString())
|
||||
}.toTypedArray(),
|
||||
),
|
||||
)
|
||||
|
||||
private class SortFilter : UriSelectFilter(
|
||||
"Sort",
|
||||
"orderby",
|
||||
arrayOf(
|
||||
Pair("a-z", "A-Z"),
|
||||
Pair("views_a", "Views all-time"),
|
||||
Pair("views_y", "Views last 365 days"),
|
||||
Pair("views_s", "Views last 180 days"),
|
||||
Pair("views_t", "Views last 90 days"),
|
||||
Pair("rating", "Rating"),
|
||||
Pair("update", "Latest"),
|
||||
Pair("create", "New manga"),
|
||||
),
|
||||
firstIsUnspecified = false,
|
||||
defaultValue = 1,
|
||||
)
|
||||
|
||||
/**
|
||||
* Class that creates a select filter. Each entry in the dropdown has a name and a display name.
|
||||
* If an entry is selected it is appended as a query parameter onto the end of the URI.
|
||||
* If `firstIsUnspecified` is set to true, if the first entry is selected, nothing will be appended on the the URI.
|
||||
*/
|
||||
// vals: <name, display>
|
||||
private open class UriSelectFilter(
|
||||
displayName: String,
|
||||
val uriParam: String,
|
||||
val vals: Array<Pair<String, String>>,
|
||||
val firstIsUnspecified: Boolean = true,
|
||||
defaultValue: Int = 0,
|
||||
) :
|
||||
Filter.Select<String>(displayName, vals.map { it.second }.toTypedArray(), defaultValue), UriFilter {
|
||||
override fun addToUri(uri: Uri.Builder) {
|
||||
if (state != 0 || !firstIsUnspecified) {
|
||||
uri.appendQueryParameter(uriParam, vals[state].first)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a filter that is able to modify a URI.
|
||||
*/
|
||||
private interface UriFilter {
|
||||
fun addToUri(uri: Uri.Builder)
|
||||
}
|
||||
|
||||
// Preferences
|
||||
|
||||
private val preferences: SharedPreferences by lazy {
|
||||
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
|
||||
}
|
||||
|
||||
override fun setupPreferenceScreen(screen: androidx.preference.PreferenceScreen) {
|
||||
val myPref = androidx.preference.ListPreference(screen.context).apply {
|
||||
key = SOURCE_PREF_TITLE
|
||||
title = SOURCE_PREF_TITLE
|
||||
entries = sourceArray.map { it.first }.toTypedArray()
|
||||
entryValues = sourceArray.map { it.second }.toTypedArray()
|
||||
summary = "%s"
|
||||
|
||||
setOnPreferenceChangeListener { _, newValue ->
|
||||
val selected = newValue as String
|
||||
val index = this.findIndexOfValue(selected)
|
||||
val entry = entryValues[index] as String
|
||||
preferences.edit().putString(SOURCE_PREF, entry).commit()
|
||||
}
|
||||
}
|
||||
screen.addPreference(myPref)
|
||||
}
|
||||
|
||||
private fun getSourcePref(): String? = preferences.getString(SOURCE_PREF, "all")
|
||||
|
||||
companion object {
|
||||
private const val SOURCE_PREF_TITLE = "Chapter List Source"
|
||||
private const val SOURCE_PREF = "Manga_Park_Source"
|
||||
private val sourceArray = arrayOf(
|
||||
Pair("All sources, all chapters", "all"),
|
||||
Pair("Source with most chapters", "most"),
|
||||
Pair("Smart list", "smart"),
|
||||
Pair("Prioritize source: Rock", "rock"),
|
||||
Pair("Prioritize source: Duck", "duck"),
|
||||
Pair("Prioritize source: Mini", "mini"),
|
||||
Pair("Prioritize source: Fox", "fox"),
|
||||
Pair("Prioritize source: Panda", "panda"),
|
||||
)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue