Remove defunct extensions (2/2) (#1824)

Remove defunct extensions (2/2)
This commit is contained in:
Mike 2019-11-25 10:36:05 -05:00 committed by arkon
parent 32f41c96d1
commit ae1c22773a
24 changed files with 0 additions and 699 deletions

View File

@ -1,12 +0,0 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
ext {
appName = 'Tachiyomi: GetManhwa'
pkgNameSuffix = 'en.getmanhwa'
extClass = '.GetManhwa'
extVersionCode = 1
libVersion = '1.2'
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 157 KiB

View File

@ -1,254 +0,0 @@
package eu.kanade.tachiyomi.extension.en.getmanhwa
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.source.model.*
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import java.text.SimpleDateFormat
import java.util.*
class GetManhwa : ParsedHttpSource() {
override val name = "GetManhwa"
override val baseUrl = "https://getmanhwa.com"
override val lang = "en"
override val supportsLatest = true
override val client: OkHttpClient = network.cloudflareClient
// Popular
override fun popularMangaRequest(page: Int): Request {
return if (page == 1) {
GET(baseUrl, headers)
} else {
GET("$baseUrl/home/page-$page/", headers)
}
}
override fun popularMangaParse(response: Response): MangasPage {
return parseMangaDocument(response.asJsoup())
}
private fun parseMangaDocument(document: Document): MangasPage {
val mangas = mutableListOf<SManga>()
document.select(popularMangaSelector()).map{ mangas.add(popularMangaFromElement(it)) }
return MangasPage(mangas, document.select(popularMangaNextPageSelector()).isNotEmpty())
}
override fun popularMangaSelector() = "section:has(h1.elementor-heading-title) div.elementor-widget-wrap section:has(img)"
override fun popularMangaFromElement(element: Element): SManga {
val manga = SManga.create()
manga.setUrlWithoutDomain(element.select("a").attr("href"))
manga.title = element.select("div.elementor-flip-box__layer__description").text()
manga.thumbnail_url = element.select("img").attr("abs:src")
return manga
}
override fun popularMangaNextPageSelector() = "i.fa-angle-right"
// Latest
override fun latestUpdatesRequest(page: Int): Request {
return GET(baseUrl, headers)
}
override fun latestUpdatesSelector() = "div.elementor-widget-wrap:contains(recent episodes) [data-column-clickable]"
override fun latestUpdatesFromElement(element: Element): SManga {
val manga = SManga.create()
manga.setUrlWithoutDomain(element.attr("data-column-clickable").substringBeforeLast("chapter"))
manga.title = element.select("div.elementor-flip-box__layer__description").first().text()
return manga
}
override fun latestUpdatesNextPageSelector() = "a.next:has(i.fa-angle-right)"
// Search
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
return if (query.isNotEmpty()) {
GET("$baseUrl/?s=$query", headers)
} else {
lateinit var genre: String
filters.forEach { filter ->
when (filter) {
is GenreFilter -> {
genre = filter.toUriPart()
}
}
}
GET("$baseUrl/genre-$genre/",headers)
}
}
override fun searchMangaParse(response: Response): MangasPage {
val document = response.asJsoup()
return if (document.location().contains("?s=")) {
// for search by query
val mangas = mutableListOf<SManga>()
document.select(searchMangaSelector()).map{ e -> mangas.add(searchMangaFromElement(e)) }
MangasPage(mangas, document.select(searchMangaNextPageSelector()).isNotEmpty())
} else {
// for search by genre
parseMangaDocument(document)
}
}
override fun searchMangaSelector() = "div.search-entry-inner"
override fun searchMangaFromElement(element: Element): SManga {
val manga = SManga.create()
element.select("h2 a").let{
manga.title = it.text()
manga.setUrlWithoutDomain(it.attr("href"))
}
manga.thumbnail_url = element.select("img").attr("abs:src")
return manga
}
override fun searchMangaNextPageSelector() = "not using this"
// Manga summary page
override fun mangaDetailsParse(document: Document): SManga {
val infoElement = document.select("div.elementor-row:contains(creator)")
val manga = SManga.create()
manga.title = infoElement.select("h2:not(:has(a))").first().text()
manga.author = infoElement.select("div.elementor-text-editor:contains(creator) p").first().text().substringAfter("Creator: ")
val status = infoElement.select("span.elementor-button-text").text()
manga.status = parseStatus(status)
manga.genre = infoElement.select("div.elementor-clearfix a").text().replace(" ", ", ")
manga.description = infoElement.select("div.elementor-clearfix:not(:contains(creator))").text()
manga.thumbnail_url = infoElement.select("img").attr("abs:src")
return manga
}
private fun parseStatus(status: String?) = when (status?.toLowerCase()) {
null -> SManga.UNKNOWN
"monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday" -> SManga.ONGOING
"end" -> SManga.COMPLETED
else -> SManga.UNKNOWN
}
// Chapters
/** Although getmanhwa isn't madara-based per se, there's a possibility of loading a page bassed
* off of madara, hence two selectors and an if-then for chapterFromElement
*/
override fun chapterListSelector() = "[data-column-clickable]:contains(chapter), li.wp-manga-chapter"
override fun chapterListParse(response: Response): List<SChapter> {
return super.chapterListParse(response).reversed()
}
override fun chapterFromElement(element: Element): SChapter {
val chapter = SChapter.create()
if (element.hasAttr("data-id")) {
chapter.setUrlWithoutDomain(element.attr("data-column-clickable"))
chapter.name = element.text().substringBeforeLast(" ")
chapter.date_upload = parseChapterDate(element.select("div.elementor-clearfix").text().substringAfterLast(" ", "0").trim()) ?: 0
} else {
element.select("a").let{
chapter.setUrlWithoutDomain(it.attr("href"))
chapter.name = it.text()
}
chapter.date_upload = parseChapterDate(element.select("span i").text()) ?: 0
}
return chapter
}
companion object {
val dateFormat by lazy {
SimpleDateFormat("MMM dd, yyyy", Locale.US)
}
}
private fun parseChapterDate(string: String): Long? {
return when {
string == "0" -> 0
"ago" in string -> parseRelativeDate(string) ?: 0
else -> dateFormat.parse(string).time
}
}
// Subtract relative date (e.g. posted 3 days ago)
private fun parseRelativeDate(date: String): Long? {
val trimmedDate = date.substringBefore(" ago").removeSuffix("s").split(" ")
val calendar = Calendar.getInstance()
when (trimmedDate[1]) {
"month" -> calendar.apply { add(Calendar.MONTH, -trimmedDate[0].toInt()) }
"week" -> calendar.apply { add(Calendar.WEEK_OF_MONTH, -trimmedDate[0].toInt()) }
"day" -> calendar.apply { add(Calendar.DAY_OF_MONTH, -trimmedDate[0].toInt()) }
"hour" -> calendar.apply { add(Calendar.HOUR_OF_DAY, -trimmedDate[0].toInt()) }
"minute" -> calendar.apply { add(Calendar.MINUTE, -trimmedDate[0].toInt()) }
"second" -> calendar.apply { add(Calendar.SECOND, 0) }
}
return calendar.timeInMillis
}
// Pages
override fun pageListRequest(chapter: SChapter): Request {
return GET("https://getmanhwa.co/${chapter.url}")
}
override fun pageListParse(document: Document): List<Page> {
val pages = mutableListOf<Page>()
document.select("img.wp-manga-chapter-img").forEachIndexed { i, img ->
pages.add(Page(i, "", img.attr("abs:src")))
}
return pages
}
override fun imageUrlParse(document: Document): String = throw UnsupportedOperationException("Not used")
// Filters
override fun getFilterList() = FilterList(
Filter.Header("NOTE: Ignored if using text search!"),
GenreFilter()
)
private class GenreFilter: UriPartFilter("Genre", arrayOf(
Pair("All","all"),
Pair("Romance","romance"),
Pair("Drama","drama"),
Pair("Comedy","comedy"),
Pair("Fantasy","fantasy"),
Pair("Action","action"),
Pair("BL","bl"),
Pair("GL","gl"),
Pair("Horror","horror"),
Pair("School Life","school-life")
))
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
}
}

View File

@ -1,12 +0,0 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
ext {
appName = 'Tachiyomi: DoujinHentai'
pkgNameSuffix = 'es.doujinhentai'
extClass = '.DoujinHentai'
extVersionCode = 2
libVersion = '1.2'
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

View File

@ -1,117 +0,0 @@
package eu.kanade.tachiyomi.extension.es.doujinhentai
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.source.model.*
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
import okhttp3.Request
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import java.text.ParseException
import java.text.SimpleDateFormat
import java.util.*
class DoujinHentai : ParsedHttpSource() {
override val baseUrl = "https://doujinhentai.net"
override val lang = "es"
override val name = "DoujinHentai"
override val supportsLatest = true
override fun popularMangaRequest(page: Int) = GET("$baseUrl/lista-manga-hentai?orderby=views&page=$page", headers)
override fun popularMangaSelector() = "div.page-content-listing > div.page-listing-item"
override fun popularMangaFromElement(element: Element) = SManga.create().apply {
element.select("div.page-item-detail").let {
it.select("div.item-summary > div.post-title > h5 > a").let {
setUrlWithoutDomain(it.attr("href"))
title = it.text()
}
thumbnail_url = it.select("div.item-thumb > a > img").attr("data-src")
}
}
override fun popularMangaNextPageSelector() = "a[rel=next]"
override fun latestUpdatesRequest(page: Int)= GET("$baseUrl/lista-manga-hentai?orderby=last&page=$page", headers)
override fun latestUpdatesSelector() = popularMangaSelector()
override fun latestUpdatesFromElement(element: Element) = popularMangaFromElement(element)
override fun latestUpdatesNextPageSelector() = popularMangaNextPageSelector()
override fun mangaDetailsParse(document: Document) = SManga.create().apply {
document.select("div.tab-summary").let {
thumbnail_url = it.select("div.summary_image > img").attr("data-src")
it.select("div.summary_content_wrap > div.summary_content").let {
author = document.select("div.author-content > a").text()
artist = document.select("div.artist-content > a").text()
genre = document.select("div.genres-content > a").joinToString(", ") {
it.text()
}
it.select("div.post-status").let {
status = parseStatus(it.select("span.label").text().orEmpty())
}
}
}
description = document.select("div.description-summary").text().orEmpty()
}
private fun parseStatus(status: String) = when {
status.contains("Ongoing") -> SManga.ONGOING
status.contains("Complete") -> SManga.COMPLETED
else -> SManga.UNKNOWN
}
override fun chapterListSelector() = "ul.main.version-chap > li.wp-manga-chapter:not(:last-child)" // removing empty li
override fun chapterFromElement(element: Element) = SChapter.create().apply {
name = element.select("a").text()
setUrlWithoutDomain(element.select("a").attr("href"))
scanlator = element.select("span.chapter-release-date > a").text()
date_upload = parseChapterDate(element.select("span.chapter-release-date > i").text())
}
private fun parseChapterDate(date: String): Long {
val dateWords = date.split(" ")
if (dateWords.size == 3) {
return try {
SimpleDateFormat("d MMM. yyyy", Locale.ENGLISH).parse(date).time
} catch (e: ParseException) {
0L
}
}
return 0L
}
override fun pageListRequest(chapter: SChapter) = GET(baseUrl + chapter.url, headers)
override fun pageListParse(document: Document): List<Page> = mutableListOf<Page>().apply {
document.select("div#all > img.img-responsive")?.forEach {
add(Page(size, "", it.attr("data-src")))
}
}
override fun imageUrlParse(document: Document) = throw UnsupportedOperationException("Not used")
override fun searchMangaRequest(page: Int, query: String, filters: FilterList) = GET("$baseUrl/search?query=$query", headers)
override fun searchMangaSelector() = ".c-tabs-item__content .c-tabs-item__content"
override fun searchMangaFromElement(element: Element) = SManga.create().apply {
thumbnail_url = element.select("div.tab-thumb.c-image-hover > a > img").attr("data-src")
setUrlWithoutDomain(element.select("div.tab-thumb.c-image-hover > a").attr("href"))
title = element.select("div.tab-thumb.c-image-hover > a").attr("title")
}
override fun searchMangaNextPageSelector() = "#not_actually_used"
}

View File

@ -1,12 +0,0 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
ext {
appName = 'Tachiyomi: Komikgo'
pkgNameSuffix = 'id.komikgo'
extClass = '.Komikgo'
extVersionCode = 2
libVersion = '1.2'
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 216 KiB

View File

@ -1,292 +0,0 @@
package eu.kanade.tachiyomi.extension.id.komikgo
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.Page
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
import okhttp3.Headers
import okhttp3.HttpUrl
import okhttp3.OkHttpClient
import okhttp3.Request
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import java.text.ParseException
import java.text.SimpleDateFormat
import java.util.Locale
class Komikgo : ParsedHttpSource() {
override val name = "Komikgo"
override val baseUrl = "https://komikgo.com"
override val lang = "id"
override val supportsLatest = true
override val client: OkHttpClient = network.cloudflareClient
override fun popularMangaRequest(page: Int): Request {
return GET("$baseUrl/page/$page?s&post_type=wp-manga&m_orderby=views", headers)
}
override fun latestUpdatesRequest(page: Int): Request {
return GET("$baseUrl/page/$page?s&post_type=wp-manga&m_orderby=latest", headers)
}
// LIST SELECTOR
override fun popularMangaSelector() = "div.c-tabs-item__content"
override fun latestUpdatesSelector() = popularMangaSelector()
override fun searchMangaSelector() = popularMangaSelector()
// ELEMENT
override fun popularMangaFromElement(element: Element): SManga = searchMangaFromElement(element)
override fun latestUpdatesFromElement(element: Element): SManga = searchMangaFromElement(element)
// NEXT SELECTOR
override fun popularMangaNextPageSelector() = "#navigation-ajax"
override fun latestUpdatesNextPageSelector() = popularMangaNextPageSelector()
override fun searchMangaNextPageSelector() = popularMangaNextPageSelector()
override fun searchMangaFromElement(element: Element):SManga {
val manga = SManga.create()
manga.thumbnail_url = element.select("div.tab-thumb > a > img").attr("src")
element.select("div.tab-thumb > a").first().let {
manga.setUrlWithoutDomain(it.attr("href"))
manga.title = it.attr("title")
}
return manga
}
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
val url = HttpUrl.parse("$baseUrl/page/$page")!!.newBuilder()
url.addQueryParameter("post_type","wp-manga")
val pattern = "\\s+".toRegex()
val q = query.replace(pattern, "+")
if(query.length > 0){
url.addQueryParameter("s", q)
}else{
url.addQueryParameter("s", "")
}
var orderBy = ""
(if (filters.isEmpty()) getFilterList() else filters).forEach { filter ->
when (filter) {
// is Status -> url.addQueryParameter("manga_status", arrayOf("", "completed", "ongoing")[filter.state])
is GenreList -> {
val genreInclude = mutableListOf<String>()
filter.state.forEach {
if (it.state == 1) {
genreInclude.add(it.id)
}
}
if(genreInclude.isNotEmpty()){
genreInclude.forEach{ genre ->
url.addQueryParameter("genre[]", genre)
}
}
}
is StatusList ->{
val statuses = mutableListOf<String>()
filter.state.forEach {
if (it.state == 1) {
statuses.add(it.id)
}
}
if(statuses.isNotEmpty()){
statuses.forEach{ status ->
url.addQueryParameter("status[]", status)
}
}
}
is SortBy -> {
orderBy = filter.toUriPart();
url.addQueryParameter("m_orderby",orderBy)
}
is TextField -> url.addQueryParameter(filter.key, filter.state)
}
}
return GET(url.toString(), headers)
}
// max 200 results
override fun mangaDetailsParse(document: Document): SManga {
val infoElement = document.select("div.site-content").first()
val manga = SManga.create()
manga.author = infoElement.select("div.author-content")?.text()
manga.artist = infoElement.select("div.artist-content")?.text()
val genres = mutableListOf<String>()
infoElement.select("div.genres-content a").forEach { element ->
val genre = element.text()
genres.add(genre)
}
manga.genre =genres.joinToString(", ")
manga.status = parseStatus(infoElement.select("div.post-status > div:nth-child(2) div").text())
manga.description = document.select("div.description-summary")?.text()
manga.thumbnail_url = document.select("div.summary_image > a > img").attr("src")
return manga
}
private fun parseStatus(element: String): Int = when {
element.toLowerCase().contains("ongoing") -> SManga.ONGOING
element.toLowerCase().contains("completed") -> SManga.COMPLETED
else -> SManga.UNKNOWN
}
override fun chapterListSelector() = "li.wp-manga-chapter"
override fun chapterFromElement(element: Element): SChapter {
val urlElement = element.select("a").first()
val chapter = SChapter.create()
chapter.setUrlWithoutDomain(urlElement.attr("href"))
chapter.name = urlElement.text()
chapter.date_upload = element.select("span.chapter-release-date i").last()?.text()?.let {
try {
SimpleDateFormat("MMMM dd, yyyy", Locale.US).parse(it).time
} catch (e: ParseException) {
SimpleDateFormat("MMM dd, yyyy", Locale.US).parse(it).time
}
} ?: 0
return chapter
}
override fun prepareNewChapter(chapter: SChapter, manga: SManga) {
val basic = Regex("""Chapter\s([0-9]+)""")
when {
basic.containsMatchIn(chapter.name) -> {
basic.find(chapter.name)?.let {
chapter.chapter_number = it.groups[1]?.value!!.toFloat()
}
}
}
}
override fun pageListParse(document: Document): List<Page> {
val pages = mutableListOf<Page>()
var i = 0
document.select("div.reading-content * img").forEach { element ->
val url = element.attr("src")
i++
if(url.length != 0){
pages.add(Page(i, "", url))
}
}
return pages
}
override fun imageUrlParse(document: Document) = ""
override fun imageRequest(page: Page): Request {
val imgHeader = Headers.Builder().apply {
add("User-Agent", "Mozilla/5.0 (Linux; U; Android 4.1.1; en-gb; Build/KLP) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30")
add("Referer", baseUrl)
}.build()
return GET(page.imageUrl!!, imgHeader)
}
// private class Status : Filter.TriState("Completed")
private class TextField(name: String, val key: String) : Filter.Text(name)
private class SortBy : UriPartFilter("Sort by", arrayOf(
Pair("Relevance", ""),
Pair("Latest", "latest"),
Pair("A-Z", "alphabet"),
Pair("Rating", "rating"),
Pair("Trending", "trending"),
Pair("Most View", "views"),
Pair("New", "new-manga")
))
private class Genre(name: String, val id: String = name) : Filter.TriState(name)
private class GenreList(genres: List<Genre>) : Filter.Group<Genre>("Genres", genres)
private class Status(name: String, val id: String = name) : Filter.TriState(name)
private class StatusList(statuses: List<Status>) : Filter.Group<Status>("Status", statuses)
override fun getFilterList() = FilterList(
// TextField("Judul", "title"),
TextField("Author", "author"),
TextField("Year", "release"),
SortBy(),
StatusList(getStatusList()),
GenreList(getGenreList())
)
private fun getStatusList() = listOf(
Status("Completed","end"),
Status("Ongoing","on-going"),
Status("Canceled","canceled"),
Status("Onhold","on-hold")
)
private fun getGenreList() = listOf(
Genre("Adventure", "Adventure"),
Genre( "Action", "action"),
Genre( "Adventure", "adventure"),
Genre( "Cars", "cars"),
Genre( "4-Koma", "4-koma"),
Genre( "Comedy", "comedy"),
Genre( "Completed", "completed"),
Genre( "Cooking", "cooking"),
Genre( "Dementia", "dementia"),
Genre( "Demons", "demons"),
Genre( "Doujinshi", "doujinshi"),
Genre( "Drama", "drama"),
Genre( "Ecchi", "ecchi"),
Genre( "Fantasy", "fantasy"),
Genre( "Game", "game"),
Genre( "Gender Bender", "gender-bender"),
Genre( "Harem", "harem"),
Genre( "Historical", "historical"),
Genre( "Horror", "horror"),
Genre( "Isekai", "isekai"),
Genre( "Josei", "josei"),
Genre( "Kids", "kids"),
Genre( "Magic", "magic"),
Genre( "Manga", "manga"),
Genre( "Manhua", "manhua"),
Genre( "Manhwa", "manhwa"),
Genre( "Martial Arts", "martial-arts"),
Genre( "Mature", "mature"),
Genre( "Mecha", "mecha"),
Genre( "Military", "military"),
Genre( "Music", "music"),
Genre( "Mystery", "mystery"),
Genre( "Old Comic", "old-comic"),
Genre( "One Shot", "one-shot"),
Genre( "Oneshot", "oneshot"),
Genre( "Parodi", "parodi"),
Genre( "Parody", "parody"),
Genre( "Police", "police"),
Genre( "Psychological", "psychological"),
Genre( "Romance", "romance"),
Genre( "Samurai", "samurai"),
Genre( "School", "school"),
Genre( "School Life", "school-life"),
Genre( "Sci-Fi", "sci-fi"),
Genre( "Seinen", "seinen"),
Genre( "Shoujo", "shoujo"),
Genre( "Shoujo Ai", "shoujo-ai"),
Genre( "Shounen", "shounen"),
Genre( "Shounen ai", "shounen-ai"),
Genre( "Slice of Life", "slice-of-life"),
Genre( "Sports", "sports"),
Genre( "Super Power", "super-power"),
Genre( "Supernatural", "supernatural"),
Genre( "Thriller", "thriller"),
Genre( "Tragedy", "tragedy"),
Genre( "Vampire", "vampire"),
Genre( "Webtoons", "webtoons"),
Genre( "Yaoi", "yaoi"),
Genre( "Yuri", "yuri")
)
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
}
}