diff --git a/lib-multisrc/etoshore/AndroidManifest.xml b/lib-multisrc/etoshore/AndroidManifest.xml
new file mode 100644
index 000000000..1635f61cd
--- /dev/null
+++ b/lib-multisrc/etoshore/AndroidManifest.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lib-multisrc/etoshore/build.gradle.kts b/lib-multisrc/etoshore/build.gradle.kts
new file mode 100644
index 000000000..dc076cc37
--- /dev/null
+++ b/lib-multisrc/etoshore/build.gradle.kts
@@ -0,0 +1,5 @@
+plugins {
+ id("lib-multisrc")
+}
+
+baseVersionCode = 1
diff --git a/lib-multisrc/etoshore/src/eu/kanade/tachiyomi/multisrc/etoshore/Etoshore.kt b/lib-multisrc/etoshore/src/eu/kanade/tachiyomi/multisrc/etoshore/Etoshore.kt
new file mode 100644
index 000000000..c57f2aa77
--- /dev/null
+++ b/lib-multisrc/etoshore/src/eu/kanade/tachiyomi/multisrc/etoshore/Etoshore.kt
@@ -0,0 +1,247 @@
+package eu.kanade.tachiyomi.multisrc.etoshore
+
+import eu.kanade.tachiyomi.network.GET
+import eu.kanade.tachiyomi.source.model.Filter
+import eu.kanade.tachiyomi.source.model.FilterList
+import eu.kanade.tachiyomi.source.model.MangasPage
+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 okhttp3.HttpUrl.Companion.toHttpUrl
+import okhttp3.Request
+import okhttp3.Response
+import org.jsoup.Jsoup
+import org.jsoup.nodes.Document
+import org.jsoup.nodes.Element
+import rx.Observable
+
+abstract class Etoshore(
+ override val name: String,
+ override val baseUrl: String,
+ override val lang: String,
+) : ParsedHttpSource() {
+
+ override val supportsLatest = true
+
+ override val client = network.cloudflareClient
+
+ override fun headersBuilder() = super.headersBuilder()
+ .add("Referer", "$baseUrl/")
+
+ // ============================== Popular ==============================
+
+ open val popularFilter = FilterList(
+ SelectionList("", listOf(Tag(value = "views", query = "sort"))),
+ )
+
+ override fun popularMangaRequest(page: Int) = searchMangaRequest(page, "", popularFilter)
+
+ override fun popularMangaParse(response: Response) = searchMangaParse(response)
+
+ override fun popularMangaSelector() = throw UnsupportedOperationException()
+
+ override fun popularMangaNextPageSelector() = throw UnsupportedOperationException()
+
+ override fun popularMangaFromElement(element: Element) = throw UnsupportedOperationException()
+
+ // ============================== Latest ===============================
+
+ open val latestFilter = FilterList(
+ SelectionList("", listOf(Tag(value = "date", query = "sort"))),
+ )
+
+ override fun latestUpdatesRequest(page: Int) = searchMangaRequest(page, "", latestFilter)
+
+ override fun latestUpdatesParse(response: Response) = searchMangaParse(response)
+
+ override fun latestUpdatesSelector() = throw UnsupportedOperationException()
+
+ override fun latestUpdatesNextPageSelector() = throw UnsupportedOperationException()
+
+ override fun latestUpdatesFromElement(element: Element) = throw UnsupportedOperationException()
+
+ // ============================== Search ===============================
+
+ override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
+ val url = "$baseUrl/page/$page".toHttpUrl().newBuilder()
+ .addQueryParameter("s", query)
+ filters.forEach { filter ->
+ when (filter) {
+ is SelectionList -> {
+ val selected = filter.selected().takeIf { it.value.isNotBlank() }
+ ?: return@forEach
+ url.addQueryParameter(selected.query, selected.value)
+ }
+ else -> {}
+ }
+ }
+ return GET(url.build(), headers)
+ }
+
+ override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable {
+ if (query.startsWith(PREFIX_SEARCH)) {
+ val slug = query.substringAfter(PREFIX_SEARCH)
+ return fetchMangaDetails(SManga.create().apply { url = "/manga/$slug/" })
+ .map { manga -> MangasPage(listOf(manga), false) }
+ }
+
+ return super.fetchSearchManga(page, query, filters)
+ }
+
+ override fun searchMangaSelector() = ".search-posts .chapter-box .poster a"
+
+ override fun searchMangaNextPageSelector() = ".navigation .naviright:has(a)"
+
+ override fun searchMangaFromElement(element: Element) = SManga.create().apply {
+ title = element.attr("title")
+ thumbnail_url = element.selectFirst("img")?.let(::imageFromElement)
+ setUrlWithoutDomain(element.absUrl("href"))
+ }
+
+ override fun searchMangaParse(response: Response): MangasPage {
+ if (filterList.isEmpty()) {
+ filterParse(response)
+ }
+ return super.searchMangaParse(response)
+ }
+
+ // ============================== Details ===============================
+
+ override fun mangaDetailsParse(document: Document) = SManga.create().apply {
+ title = document.selectFirst("h1")!!.text()
+
+ description = document.selectFirst(".excerpt p")?.text()
+
+ document.selectFirst(".details-right-con img")?.let { thumbnail_url = imageFromElement(it) }
+
+ genre = document.select("div.meta-item span.meta-title:contains(Genres) + span a")
+ .joinToString { it.text() }
+
+ author = document.selectFirst("div.meta-item span.meta-title:contains(Author) + span a")
+ ?.text()
+
+ with(document) {
+ status = when {
+ containsClass(".finished") -> SManga.COMPLETED
+ containsClass(".publishing") -> SManga.ONGOING
+ containsClass(".on-hiatus") -> SManga.ON_HIATUS
+ containsClass(".discontinued") -> SManga.CANCELLED
+ else -> SManga.UNKNOWN
+ }
+ }
+
+ setUrlWithoutDomain(document.location())
+ }
+
+ private fun Element.containsClass(cssSelector: String) = select(cssSelector).isNotEmpty()
+
+ protected open fun imageFromElement(element: Element): String? {
+ val attributes = listOf(
+ "data-src",
+ "data-lazy-src",
+ "data-cfsrc",
+ "src",
+ )
+ return attributes
+ .mapNotNull { attr -> element.takeIf { it.hasAttr(attr) }?.attr("abs:$attr") }
+ .maxOrNull()
+ ?: element.takeIf { it.hasAttr("srcset") }?.attr("abs:srcset")?.getSrcSetImage()
+ }
+
+ protected open fun String.getSrcSetImage(): String? {
+ return this.split(" ")
+ .filter(URL_REGEX::matches)
+ .maxOfOrNull(String::toString)
+ }
+
+ // ============================== Chapters ============================
+
+ override fun chapterListSelector() = ".chapter-list li a"
+
+ override fun chapterListParse(response: Response): List {
+ return super.chapterListParse(response)
+ }
+
+ override fun chapterFromElement(element: Element) = SChapter.create().apply {
+ name = element.selectFirst(".title")!!.text()
+ setUrlWithoutDomain(element.absUrl("href"))
+ }
+
+ // ============================== Pages ===============================
+
+ override fun pageListParse(document: Document): List {
+ return document.select(".chapter-images .chapter-item > img").mapIndexed { index, element ->
+ Page(index, document.location(), imageFromElement(element))
+ }
+ }
+
+ override fun imageUrlParse(document: Document) = ""
+
+ // ============================= Filters ==============================
+
+ private var filterList = emptyList>>()
+
+ override fun getFilterList(): FilterList {
+ val filters = mutableListOf>()
+
+ filters += if (filterList.isNotEmpty()) {
+ filterList.map { SelectionList(it.first, it.second) }
+ } else {
+ listOf(Filter.Header("Aperte 'Redefinir' para tentar mostrar os filtros"))
+ }
+
+ return FilterList(filters)
+ }
+
+ protected open fun parseSelection(document: Document, selector: String): Pair>? {
+ val selectorFilter = "#filter-form $selector .select-item-head .text"
+
+ return document.selectFirst(selectorFilter)?.text()?.let { displayName ->
+ val tags = document.select("#filter-form $selector li").map { element ->
+ element.selectFirst("input")!!.let { input ->
+ Tag(
+ name = element.selectFirst(".text")!!.text(),
+ value = input.attr("value"),
+ query = input.attr("name"),
+ )
+ }
+ }
+ displayName to mutableListOf().apply {
+ this += Tag("Default")
+ this += tags
+ }
+ }
+ }
+
+ open val filterListSelector: List = listOf(
+ ".filter-genre",
+ ".filter-status",
+ ".filter-type",
+ ".filter-year",
+ ".filter-sort",
+ )
+
+ open fun filterParse(response: Response) {
+ val document = Jsoup.parseBodyFragment(response.peekBody(Long.MAX_VALUE).string())
+ filterList = filterListSelector.mapNotNull { selector -> parseSelection(document, selector) }
+ }
+
+ protected data class Tag(val name: String = "", val value: String = "", val query: String = "")
+
+ private open class SelectionList(displayName: String, private val vals: List, state: Int = 0) :
+ Filter.Select(displayName, vals.map { it.name }.toTypedArray(), state) {
+ fun selected() = vals[state]
+ }
+
+ // ============================= Utils ==============================
+
+ private fun String.containsIn(array: Array): Boolean {
+ return this.lowercase() in array.map { it.lowercase() }
+ }
+
+ companion object {
+ const val PREFIX_SEARCH = "id:"
+ val URL_REGEX = """^(https?://[^\s/$.?#].[^\s]*)$""".toRegex()
+ }
+}
diff --git a/lib-multisrc/etoshore/src/eu/kanade/tachiyomi/multisrc/etoshore/EtoshoreUrlActivity.kt b/lib-multisrc/etoshore/src/eu/kanade/tachiyomi/multisrc/etoshore/EtoshoreUrlActivity.kt
new file mode 100644
index 000000000..f31329ba5
--- /dev/null
+++ b/lib-multisrc/etoshore/src/eu/kanade/tachiyomi/multisrc/etoshore/EtoshoreUrlActivity.kt
@@ -0,0 +1,42 @@
+package eu.kanade.tachiyomi.multisrc.etoshore
+
+import android.app.Activity
+import android.content.ActivityNotFoundException
+import android.content.Intent
+import android.os.Bundle
+import android.util.Log
+import kotlin.system.exitProcess
+
+class EtoshoreUrlActivity : Activity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ val pathSegments = intent?.data?.pathSegments
+
+ if (pathSegments != null && pathSegments.size >= 2) {
+ val mainIntent = Intent().apply {
+ action = "eu.kanade.tachiyomi.SEARCH"
+ putExtra("query", "${getSLUG(pathSegments)}")
+ putExtra("filter", packageName)
+ }
+ try {
+ startActivity(mainIntent)
+ } catch (e: ActivityNotFoundException) {
+ Log.e("EtoshoreUrl", e.toString())
+ }
+ } else {
+ Log.e("EtoshoreUrl", "could not parse uri from intent $intent")
+ }
+
+ finish()
+ exitProcess(0)
+ }
+
+ private fun getSLUG(pathSegments: MutableList): String? {
+ return if (pathSegments.size >= 2) {
+ val slug = pathSegments[1]
+ "${Etoshore.PREFIX_SEARCH}$slug"
+ } else {
+ null
+ }
+ }
+}
diff --git a/src/pt/nextscan/build.gradle b/src/pt/nextscan/build.gradle
new file mode 100644
index 000000000..2b5551959
--- /dev/null
+++ b/src/pt/nextscan/build.gradle
@@ -0,0 +1,10 @@
+ext {
+ extName = 'Next Scan'
+ extClass = '.NextScan'
+ themePkg = 'etoshore'
+ baseUrl = 'https://nextscan.cloud'
+ overrideVersionCode = 0
+ isNsfw = true
+}
+
+apply from: "$rootDir/common.gradle"
diff --git a/src/pt/nextscan/res/mipmap-hdpi/ic_launcher.png b/src/pt/nextscan/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 000000000..742686639
Binary files /dev/null and b/src/pt/nextscan/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/src/pt/nextscan/res/mipmap-mdpi/ic_launcher.png b/src/pt/nextscan/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 000000000..03bca957f
Binary files /dev/null and b/src/pt/nextscan/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/src/pt/nextscan/res/mipmap-xhdpi/ic_launcher.png b/src/pt/nextscan/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..834f1a96d
Binary files /dev/null and b/src/pt/nextscan/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/src/pt/nextscan/res/mipmap-xxhdpi/ic_launcher.png b/src/pt/nextscan/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 000000000..b64b2a815
Binary files /dev/null and b/src/pt/nextscan/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/src/pt/nextscan/res/mipmap-xxxhdpi/ic_launcher.png b/src/pt/nextscan/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 000000000..121ef4aa6
Binary files /dev/null and b/src/pt/nextscan/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/src/pt/nextscan/src/eu/kanade/tachiyomi/extension/pt/nextscan/NextScan.kt b/src/pt/nextscan/src/eu/kanade/tachiyomi/extension/pt/nextscan/NextScan.kt
new file mode 100644
index 000000000..33d10c038
--- /dev/null
+++ b/src/pt/nextscan/src/eu/kanade/tachiyomi/extension/pt/nextscan/NextScan.kt
@@ -0,0 +1,19 @@
+package eu.kanade.tachiyomi.extension.pt.nextscan
+
+import eu.kanade.tachiyomi.multisrc.etoshore.Etoshore
+import eu.kanade.tachiyomi.network.interceptor.rateLimit
+import eu.kanade.tachiyomi.source.model.SChapter
+import okhttp3.Response
+
+class NextScan : Etoshore(
+ "Next Scan",
+ "https://nextscan.cloud",
+ "pt-BR",
+) {
+ override val client = super.client.newBuilder()
+ .rateLimit(3)
+ .build()
+
+ override fun chapterListParse(response: Response): List =
+ super.chapterListParse(response).reversed()
+}
diff --git a/src/tr/anisamanga/build.gradle b/src/tr/anisamanga/build.gradle
index 811746d75..683e944e1 100644
--- a/src/tr/anisamanga/build.gradle
+++ b/src/tr/anisamanga/build.gradle
@@ -1,7 +1,9 @@
ext {
extName = 'Anisa Manga'
extClass = '.AnisaManga'
- extVersionCode = 43
+ themePkg = 'etoshore'
+ baseUrl = 'https://anisamanga.net'
+ overrideVersionCode = 43
isNsfw = true
}
diff --git a/src/tr/anisamanga/src/eu/kanade/tachiyomi/extension/tr/anisamanga/AnisaManga.kt b/src/tr/anisamanga/src/eu/kanade/tachiyomi/extension/tr/anisamanga/AnisaManga.kt
index b868f4a98..576e0408f 100644
--- a/src/tr/anisamanga/src/eu/kanade/tachiyomi/extension/tr/anisamanga/AnisaManga.kt
+++ b/src/tr/anisamanga/src/eu/kanade/tachiyomi/extension/tr/anisamanga/AnisaManga.kt
@@ -1,34 +1,16 @@
package eu.kanade.tachiyomi.extension.tr.anisamanga
-import eu.kanade.tachiyomi.network.GET
+import eu.kanade.tachiyomi.multisrc.etoshore.Etoshore
import eu.kanade.tachiyomi.network.interceptor.rateLimit
-import eu.kanade.tachiyomi.source.model.Filter
-import eu.kanade.tachiyomi.source.model.FilterList
-import eu.kanade.tachiyomi.source.model.MangasPage
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 okhttp3.HttpUrl.Companion.toHttpUrl
-import okhttp3.Request
-import okhttp3.Response
-import okio.IOException
-import org.jsoup.Jsoup
import org.jsoup.nodes.Document
-import org.jsoup.nodes.Element
-import rx.Observable
-
-// Etoshore
-class AnisaManga : ParsedHttpSource() {
-
- override val name = "Anisa Manga"
-
- override val baseUrl = "https://anisamanga.net"
-
- override val lang = "tr"
-
- override val supportsLatest = true
+import java.io.IOException
+class AnisaManga : Etoshore(
+ "Anisa Manga",
+ "https://anisamanga.net",
+ "tr",
+) {
// Migrate from Madara to Etoshore
override val versionId = 2
@@ -36,175 +18,9 @@ class AnisaManga : ParsedHttpSource() {
.rateLimit(2)
.build()
- override fun headersBuilder() = super.headersBuilder()
- .add("Referer", "$baseUrl/")
-
- // ============================== Popular ==============================
-
- open val popularFilter = FilterList(
- SelectionList("", listOf(Tag(value = "views", query = "sort"))),
- )
-
- override fun popularMangaRequest(page: Int) = searchMangaRequest(page, "", popularFilter)
-
- override fun popularMangaParse(response: Response) = searchMangaParse(response)
-
- override fun popularMangaSelector() = throw UnsupportedOperationException()
-
- override fun popularMangaNextPageSelector() = throw UnsupportedOperationException()
-
- override fun popularMangaFromElement(element: Element) = throw UnsupportedOperationException()
-
- // ============================== Latest ===============================
-
- open val latestFilter = FilterList(
- SelectionList("", listOf(Tag(value = "date", query = "sort"))),
- )
-
- override fun latestUpdatesRequest(page: Int) = searchMangaRequest(page, "", latestFilter)
-
- override fun latestUpdatesParse(response: Response) = searchMangaParse(response)
-
- override fun latestUpdatesSelector() = throw UnsupportedOperationException()
-
- override fun latestUpdatesNextPageSelector() = throw UnsupportedOperationException()
-
- override fun latestUpdatesFromElement(element: Element) = throw UnsupportedOperationException()
-
- // ============================== Search ===============================
-
- override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
- val url = "$baseUrl/page/$page".toHttpUrl().newBuilder()
- .addQueryParameter("s", query)
- filters.forEach { filter ->
- when (filter) {
- is SelectionList -> {
- val selected = filter.selected().takeIf { it.value.isNotBlank() }
- ?: return@forEach
- url.addQueryParameter(selected.query, selected.value)
- }
- else -> {}
- }
- }
- return GET(url.build(), headers)
- }
-
- override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable {
- if (query.startsWith(PREFIX_SEARCH)) {
- val slug = query.substringAfter(PREFIX_SEARCH)
- return fetchMangaDetails(SManga.create().apply { url = "/manga/$slug/" })
- .map { manga -> MangasPage(listOf(manga), false) }
- }
-
- return super.fetchSearchManga(page, query, filters)
- }
-
- override fun searchMangaSelector() = ".search-posts .chapter-box .poster a"
-
- override fun searchMangaNextPageSelector() = ".navigation .naviright:has(a)"
-
- override fun searchMangaFromElement(element: Element) = SManga.create().apply {
- title = element.attr("title")
- thumbnail_url = element.selectFirst("img")?.let(::imageFromElement)
- setUrlWithoutDomain(element.absUrl("href"))
- }
-
- override fun searchMangaParse(response: Response): MangasPage {
- if (filterList.isEmpty()) {
- filterParse(response)
- }
- return super.searchMangaParse(response)
- }
-
- // ============================== Details ===============================
-
- override fun mangaDetailsParse(document: Document) = SManga.create().apply {
- title = document.selectFirst("h1")!!.text()
-
- description = document.selectFirst(".excerpt p")?.text()
-
- document.selectFirst(".details-right-con img")?.let { thumbnail_url = imageFromElement(it) }
-
- genre = document.select("div.meta-item span.meta-title:contains(Genres) + span a")
- .joinToString { it.text() }
-
- author = document.selectFirst("div.meta-item span.meta-title:contains(Author) + span a")
-
- ?.text()
-
- document.selectFirst(".status")?.text()?.let {
- status = it.toMangaStatus()
- }
-
- setUrlWithoutDomain(document.location())
- }
-
- protected open fun imageFromElement(element: Element): String? {
- return when {
- element.hasAttr("data-src") -> element.attr("abs:data-src")
- element.hasAttr("data-lazy-src") -> element.attr("abs:data-lazy-src")
- element.hasAttr("srcset") -> element.attr("abs:srcset").getSrcSetImage()
- element.hasAttr("data-cfsrc") -> element.attr("abs:data-cfsrc")
- else -> element.attr("abs:src")
- }
- }
-
- protected open fun String.getSrcSetImage(): String? {
- return this.split(" ")
- .filter(URL_REGEX::matches)
- .maxOfOrNull(String::toString)
- }
-
- protected val completedStatusList: Array = arrayOf(
- "Finished",
- "Completo",
- )
-
- protected open val ongoingStatusList: Array = arrayOf(
- "Publishing",
- "Ativo",
- )
-
- protected val hiatusStatusList: Array = arrayOf(
- "on hiatus",
- )
-
- protected val canceledStatusList: Array = arrayOf(
- "Canceled",
- "Discontinued",
- )
-
- open fun String.toMangaStatus(): Int {
- return when {
- containsIn(completedStatusList) -> SManga.COMPLETED
- containsIn(ongoingStatusList) -> SManga.ONGOING
- containsIn(hiatusStatusList) -> SManga.ON_HIATUS
- containsIn(canceledStatusList) -> SManga.CANCELLED
- else -> SManga.UNKNOWN
- }
- }
-
- // ============================== Chapters ============================
-
- override fun chapterListSelector() = ".chapter-list li a"
-
- override fun chapterListParse(response: Response): List {
- return super.chapterListParse(response)
- }
-
- override fun chapterFromElement(element: Element) = SChapter.create().apply {
- name = element.selectFirst(".title")!!.text()
- setUrlWithoutDomain(element.absUrl("href"))
- }
-
- // ============================== Pages ===============================
-
override fun pageListParse(document: Document): List {
verifyLoginRequired(document)
-
- return document.select(".chapter-images .chapter-item > img").mapIndexed { index, element ->
- Page(index, document.location(), imageFromElement(element))
- }
+ return super.pageListParse(document)
}
private fun verifyLoginRequired(document: Document) {
@@ -213,73 +29,4 @@ class AnisaManga : ParsedHttpSource() {
throw IOException("Web görünümünde oturum açın")
}
}
-
- override fun imageUrlParse(document: Document) = ""
-
- // ============================= Filters ==============================
-
- private var filterList = emptyList>>()
-
- override fun getFilterList(): FilterList {
- val filters = mutableListOf>()
-
- filters += if (filterList.isNotEmpty()) {
- filterList.map { SelectionList(it.first, it.second) }
- } else {
- listOf(Filter.Header("Aperte 'Redefinir' para tentar mostrar os filtros"))
- }
-
- return FilterList(filters)
- }
-
- protected open fun parseSelection(document: Document, selector: String): Pair>? {
- val selectorFilter = "#filter-form $selector .select-item-head .text"
-
- return document.selectFirst(selectorFilter)?.text()?.let { displayName ->
- val tags = document.select("#filter-form $selector li").map { element ->
- element.selectFirst("input")!!.let { input ->
- Tag(
- name = element.selectFirst(".text")!!.text(),
- value = input.attr("value"),
- query = input.attr("name"),
- )
- }
- }
- displayName to mutableListOf().apply {
- this += Tag("Default")
- this += tags
- }
- }
- }
-
- open val filterListSelector: List = listOf(
- ".filter-genre",
- ".filter-status",
- ".filter-type",
- ".filter-year",
- ".filter-sort",
- )
-
- open fun filterParse(response: Response) {
- val document = Jsoup.parseBodyFragment(response.peekBody(Long.MAX_VALUE).string())
- filterList = filterListSelector.mapNotNull { selector -> parseSelection(document, selector) }
- }
-
- protected data class Tag(val name: String = "", val value: String = "", val query: String = "")
-
- private open class SelectionList(displayName: String, private val vals: List, state: Int = 0) :
- Filter.Select(displayName, vals.map { it.name }.toTypedArray(), state) {
- fun selected() = vals[state]
- }
-
- // ============================= Utils ==============================
-
- private fun String.containsIn(array: Array): Boolean {
- return this.lowercase() in array.map { it.lowercase() }
- }
-
- companion object {
- const val PREFIX_SEARCH = "id:"
- val URL_REGEX = """^(https?://[^\s/$.?#].[^\s]*)${'$'}""".toRegex()
- }
}