Use SimplyHentai API (#9306)
This commit is contained in:
parent
83f8e828bf
commit
421fde6d82
|
@ -6,7 +6,7 @@ ext {
|
||||||
extName = 'Simply Hentai'
|
extName = 'Simply Hentai'
|
||||||
pkgNameSuffix = 'all.simplyhentai'
|
pkgNameSuffix = 'all.simplyhentai'
|
||||||
extClass = '.SimplyHentaiFactory'
|
extClass = '.SimplyHentaiFactory'
|
||||||
extVersionCode = 4
|
extVersionCode = 5
|
||||||
containsNsfw = true
|
containsNsfw = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,221 +1,229 @@
|
||||||
package eu.kanade.tachiyomi.extension.all.simplyhentai
|
package eu.kanade.tachiyomi.extension.all.simplyhentai
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.app.Application
|
||||||
|
import android.net.Uri
|
||||||
|
import androidx.preference.EditTextPreference
|
||||||
|
import androidx.preference.PreferenceScreen
|
||||||
import eu.kanade.tachiyomi.network.GET
|
import eu.kanade.tachiyomi.network.GET
|
||||||
import eu.kanade.tachiyomi.source.model.Filter
|
import eu.kanade.tachiyomi.network.asObservableSuccess
|
||||||
|
import eu.kanade.tachiyomi.source.ConfigurableSource
|
||||||
import eu.kanade.tachiyomi.source.model.FilterList
|
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.Page
|
||||||
import eu.kanade.tachiyomi.source.model.SChapter
|
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.HttpSource
|
||||||
import eu.kanade.tachiyomi.util.asJsoup
|
import kotlinx.serialization.ExperimentalSerializationApi
|
||||||
|
import kotlinx.serialization.decodeFromString
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import kotlinx.serialization.json.jsonObject
|
|
||||||
import kotlinx.serialization.json.jsonPrimitive
|
|
||||||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
|
||||||
import okhttp3.OkHttpClient
|
|
||||||
import okhttp3.Request
|
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
import org.jsoup.nodes.Document
|
import uy.kohesive.injekt.Injekt
|
||||||
import org.jsoup.nodes.Element
|
import uy.kohesive.injekt.api.get
|
||||||
import uy.kohesive.injekt.injectLazy
|
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
abstract class SimplyHentai(
|
@ExperimentalSerializationApi
|
||||||
override val lang: String,
|
open class SimplyHentai(override val lang: String) : ConfigurableSource, HttpSource() {
|
||||||
private val urlLang: String,
|
|
||||||
private val searchLang: String
|
|
||||||
) : ParsedHttpSource() {
|
|
||||||
override val name = "Simply Hentai"
|
override val name = "Simply Hentai"
|
||||||
|
|
||||||
override val baseUrl = "https://old.simply-hentai.com"
|
override val baseUrl = "https://www.simply-hentai.com"
|
||||||
|
|
||||||
override val supportsLatest = true
|
override val supportsLatest = true
|
||||||
|
|
||||||
override val client: OkHttpClient = network.cloudflareClient
|
override val client = network.cloudflareClient
|
||||||
|
|
||||||
private val json: Json by injectLazy()
|
override val versionId = 2
|
||||||
|
|
||||||
// Popular
|
private val apiUrl = "https://api.simply-hentai.com/v3"
|
||||||
|
|
||||||
override fun popularMangaRequest(page: Int): Request {
|
private val langName by lazy {
|
||||||
return GET("$baseUrl/album/language/$urlLang/$page/popularity/desc", headers)
|
Locale.forLanguageTag(lang).displayName
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun popularMangaSelector() = "div.col-sm-3"
|
private val json by lazy { Injekt.get<Json>() }
|
||||||
|
|
||||||
override fun popularMangaFromElement(element: Element): SManga {
|
private val preferences by lazy {
|
||||||
val manga = SManga.create()
|
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)!!
|
||||||
|
|
||||||
element.select("h3.object-title a").let {
|
|
||||||
manga.url = it.attr("href").substringAfter(baseUrl)
|
|
||||||
manga.title = it.text()
|
|
||||||
}
|
|
||||||
manga.thumbnail_url = element.select("img.img-responsive").attr("abs:data-src")
|
|
||||||
|
|
||||||
return manga
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun popularMangaNextPageSelector() = "a[rel=next]"
|
override fun popularMangaRequest(page: Int) =
|
||||||
|
Uri.parse("$apiUrl/albums").buildUpon().run {
|
||||||
// Latest
|
appendQueryParameter("si", "0")
|
||||||
|
appendQueryParameter("locale", lang)
|
||||||
override fun latestUpdatesRequest(page: Int): Request {
|
appendQueryParameter("language", langName)
|
||||||
return GET("$baseUrl/album/language/$urlLang/$page", headers)
|
appendQueryParameter("sort", "spotlight")
|
||||||
|
appendQueryParameter("page", page.toString())
|
||||||
|
GET(build().toString(), headers)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun latestUpdatesSelector() = popularMangaSelector()
|
override fun popularMangaParse(response: Response) =
|
||||||
|
response.decode<SHList<SHObject>>().run {
|
||||||
|
MangasPage(
|
||||||
|
data.map {
|
||||||
|
SManga.create().apply {
|
||||||
|
url = it.path
|
||||||
|
title = it.title
|
||||||
|
thumbnail_url = it.preview.sizes.thumb
|
||||||
|
}
|
||||||
|
},
|
||||||
|
pagination.next != null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
override fun latestUpdatesFromElement(element: Element): SManga = popularMangaFromElement(element)
|
override fun latestUpdatesRequest(page: Int) =
|
||||||
|
Uri.parse("$apiUrl/albums").buildUpon().run {
|
||||||
|
appendQueryParameter("si", "0")
|
||||||
|
appendQueryParameter("locale", lang)
|
||||||
|
appendQueryParameter("language", langName)
|
||||||
|
appendQueryParameter("sort", "newest")
|
||||||
|
appendQueryParameter("page", page.toString())
|
||||||
|
GET(build().toString(), headers)
|
||||||
|
}
|
||||||
|
|
||||||
override fun latestUpdatesNextPageSelector() = popularMangaNextPageSelector()
|
override fun latestUpdatesParse(response: Response) =
|
||||||
|
popularMangaParse(response)
|
||||||
|
|
||||||
// Search
|
override fun searchMangaRequest(page: Int, query: String, filters: FilterList) =
|
||||||
|
Uri.parse("$apiUrl/search/complex").buildUpon().run {
|
||||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
appendQueryParameter("si", "0")
|
||||||
val url = "$baseUrl/search".toHttpUrlOrNull()!!.newBuilder()
|
appendQueryParameter("locale", lang)
|
||||||
.addQueryParameter("query", query)
|
appendQueryParameter("query", query)
|
||||||
.addQueryParameter("language_ids[$searchLang]", searchLang)
|
appendQueryParameter("page", page.toString())
|
||||||
.addQueryParameter("page", page.toString())
|
appendQueryParameter("blacklist", blacklist)
|
||||||
|
appendQueryParameter("filter[languages][0]", langName)
|
||||||
(if (filters.isEmpty()) getFilterList() else filters).forEach { filter ->
|
filters.forEach { filter ->
|
||||||
when (filter) {
|
when (filter) {
|
||||||
is GenreList -> {
|
is SortFilter -> {
|
||||||
filter.state.forEach {
|
appendQueryParameter("sort", filter.orders[filter.state])
|
||||||
if (it.state) url.addQueryParameter("tag_ids[${it.id}]", it.id)
|
}
|
||||||
}
|
is SeriesFilter -> filter.value?.let {
|
||||||
}
|
appendQueryParameter("filter[series_title][0]", it)
|
||||||
is SeriesList -> {
|
}
|
||||||
filter.state.forEach {
|
is TagsFilter -> filter.value?.forEachIndexed { idx, tag ->
|
||||||
if (it.state) url.addQueryParameter("series_id[${it.id}]", it.id)
|
appendQueryParameter("filter[tags][$idx]", tag.trim())
|
||||||
}
|
}
|
||||||
}
|
is ArtistsFilter -> filter.value?.forEachIndexed { idx, tag ->
|
||||||
is SortOrder -> {
|
appendQueryParameter("filter[artists][$idx]", tag.trim())
|
||||||
url.addQueryParameter("sort", getSortOrder()[filter.state].second)
|
}
|
||||||
|
is TranslatorsFilter -> filter.value?.forEachIndexed { idx, tag ->
|
||||||
|
appendQueryParameter("filter[translators][$idx]", tag.trim())
|
||||||
|
}
|
||||||
|
is CharactersFilter -> filter.value?.forEachIndexed { idx, tag ->
|
||||||
|
appendQueryParameter("filter[characters][$idx]", tag.trim())
|
||||||
|
}
|
||||||
|
else -> Unit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
GET(build().toString(), headers)
|
||||||
}
|
}
|
||||||
|
|
||||||
return GET(url.toString(), headers)
|
override fun searchMangaParse(response: Response) =
|
||||||
|
response.decode<SHList<SHWrapper>>().run {
|
||||||
|
MangasPage(
|
||||||
|
data.map {
|
||||||
|
SManga.create().apply {
|
||||||
|
url = it.`object`.path
|
||||||
|
title = it.`object`.title
|
||||||
|
thumbnail_url = it.`object`.preview.sizes.thumb
|
||||||
|
}
|
||||||
|
},
|
||||||
|
pagination.next != null
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun searchMangaSelector() = popularMangaSelector()
|
override fun mangaDetailsRequest(manga: SManga) =
|
||||||
|
GET(baseUrl + manga.url, headers)
|
||||||
|
|
||||||
override fun searchMangaFromElement(element: Element) = popularMangaFromElement(element)
|
override fun fetchMangaDetails(manga: SManga) =
|
||||||
|
client.newCall(chapterListRequest(manga))
|
||||||
|
.asObservableSuccess().map(::mangaDetailsParse)!!
|
||||||
|
|
||||||
override fun searchMangaNextPageSelector() = popularMangaNextPageSelector()
|
override fun mangaDetailsParse(response: Response) =
|
||||||
|
SManga.create().apply {
|
||||||
// Details
|
val album = response.decode<SHAlbum>().data
|
||||||
|
url = album.path
|
||||||
override fun mangaDetailsParse(document: Document): SManga {
|
title = album.title
|
||||||
val manga = SManga.create()
|
description = buildString {
|
||||||
|
if (!album.description.isNullOrEmpty()) {
|
||||||
document.select("div.padding-md-right-8").let { info ->
|
append("${album.description}\n\n")
|
||||||
manga.artist = info.select("div.box-title:contains(Artists) + a").text()
|
|
||||||
manga.author = manga.artist
|
|
||||||
manga.genre = info.select("a[rel=tag]").joinToString { it.text() }
|
|
||||||
manga.description = info.select("div.link-box > div.box-title:contains(Series) ~ a").let { e ->
|
|
||||||
if (e.text().isNotEmpty()) "Series: ${e.joinToString { it.text() }}\n\n" else ""
|
|
||||||
}
|
}
|
||||||
manga.description += info.select("div.link-box > div.box-title:contains(Characters) ~ a").let { e ->
|
append("Series: ${album.series.title}\n")
|
||||||
if (e.text().isNotEmpty()) ("Characters: ${e.joinToString { it.text() }}\n\n") else ""
|
album.characters.joinTo(this, prefix = "Characters: ") { it.title }
|
||||||
}
|
}
|
||||||
manga.status = SManga.COMPLETED
|
thumbnail_url = album.preview.sizes.thumb
|
||||||
}
|
genre = album.tags.joinToString { it.title }
|
||||||
manga.thumbnail_url = document.select("div.col-xs-12 img.img-responsive").attr("abs:data-src")
|
artist = album.artists.joinToString { it.title }
|
||||||
|
author = artist
|
||||||
return manga
|
initialized = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Chapters
|
override fun chapterListRequest(manga: SManga) =
|
||||||
|
Uri.parse("$apiUrl/album").buildUpon().run {
|
||||||
|
appendEncodedPath(manga.url.split('/')[2])
|
||||||
|
appendQueryParameter("si", "0")
|
||||||
|
appendQueryParameter("locale", lang)
|
||||||
|
GET(build().toString(), headers)
|
||||||
|
}
|
||||||
|
|
||||||
override fun chapterListParse(response: Response): List<SChapter> {
|
override fun chapterListParse(response: Response) =
|
||||||
return listOf(
|
|
||||||
SChapter.create().apply {
|
SChapter.create().apply {
|
||||||
|
val album = response.decode<SHAlbum>().data
|
||||||
name = "Chapter"
|
name = "Chapter"
|
||||||
url = response.request.url.toString().removeSuffix("/").substringAfterLast("/")
|
chapter_number = -1f
|
||||||
chapter_number = 1f
|
url = "${album.path}/all-pages"
|
||||||
|
scanlator = album.translators.joinToString { it.title }
|
||||||
|
date_upload = dateFormat.parse(album.created_at)?.time ?: 0L
|
||||||
|
}.let(::listOf)
|
||||||
|
|
||||||
date_upload = response.asJsoup().select(".stat-container div:contains(Uploaded) div.bold")?.text().let {
|
override fun pageListRequest(chapter: SChapter) =
|
||||||
DATE_FORMAT.parse(it!!)?.time
|
Uri.parse("$apiUrl/album").buildUpon().run {
|
||||||
} ?: 0L
|
appendEncodedPath(chapter.url.split('/')[2])
|
||||||
}
|
appendQueryParameter("si", "0")
|
||||||
)
|
appendQueryParameter("locale", lang)
|
||||||
|
GET(build().toString(), headers)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun chapterListSelector() = "not used"
|
override fun pageListParse(response: Response) =
|
||||||
|
response.decode<SHAlbum>().data.images.map {
|
||||||
override fun chapterFromElement(element: Element): SChapter = throw UnsupportedOperationException("Not used")
|
Page(it.page_num, "", it.sizes.full)
|
||||||
|
|
||||||
// Pages
|
|
||||||
|
|
||||||
override fun pageListRequest(chapter: SChapter): Request {
|
|
||||||
return GET("https://api.simply-hentai.com/v1/images/album/${chapter.url}", headersBuilder().add("X-Requested-With", "XMLHttpRequest").build())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun pageListParse(response: Response): List<Page> {
|
|
||||||
val pages = mutableListOf<Page>()
|
|
||||||
|
|
||||||
json.parseToJsonElement(response.body!!.string()).jsonObject.forEach {
|
|
||||||
pages.add(
|
|
||||||
Page(
|
|
||||||
pages.size, "",
|
|
||||||
it.value.jsonObject["sizes"]!!.jsonObject["full"]!!.jsonPrimitive.content
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return pages
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun pageListParse(document: Document): List<Page> = throw UnsupportedOperationException("Not used")
|
|
||||||
|
|
||||||
override fun imageUrlParse(document: Document): String = throw UnsupportedOperationException("Not used")
|
|
||||||
|
|
||||||
// Filters
|
|
||||||
|
|
||||||
private class SortOrder(sortPairs: List<Pair<String, String>>) : Filter.Select<String>("Sort By", sortPairs.map { it.first }.toTypedArray())
|
|
||||||
private class SearchPair(name: String, val id: String = name) : Filter.CheckBox(name)
|
|
||||||
private class GenreList(searchVal: List<SearchPair>) : Filter.Group<SearchPair>("Genres", searchVal)
|
|
||||||
private class SeriesList(searchVal: List<SearchPair>) : Filter.Group<SearchPair>("Series", searchVal)
|
|
||||||
|
|
||||||
override fun getFilterList() = FilterList(
|
override fun getFilterList() = FilterList(
|
||||||
SortOrder(getSortOrder()),
|
SortFilter(),
|
||||||
GenreList(getGenreList()),
|
SeriesFilter(),
|
||||||
SeriesList(getSeriesList())
|
Note("tags"),
|
||||||
|
TagsFilter(),
|
||||||
|
Note("artists"),
|
||||||
|
ArtistsFilter(),
|
||||||
|
Note("translators"),
|
||||||
|
TranslatorsFilter(),
|
||||||
|
Note("characters"),
|
||||||
|
CharactersFilter(),
|
||||||
)
|
)
|
||||||
|
|
||||||
// "Relevance" should be empty, don't add a "Views" sort order
|
override fun setupPreferenceScreen(screen: PreferenceScreen) {
|
||||||
private fun getSortOrder() = listOf(
|
EditTextPreference(screen.context).apply {
|
||||||
Pair("Relevance", ""),
|
key = "blacklist"
|
||||||
Pair("Popularity", "sort_value"),
|
title = "Blacklist"
|
||||||
Pair("Upload Date", "created_at")
|
summary = "Separate multiple tags with commas (,)"
|
||||||
)
|
|
||||||
|
|
||||||
// TODO: add more to getGenreList and getSeriesList
|
setOnPreferenceChangeListener { _, newValue ->
|
||||||
private fun getGenreList() = listOf(
|
preferences.edit().putString("blacklist", newValue as String).commit()
|
||||||
SearchPair("Solo Female", "4807"),
|
}
|
||||||
SearchPair("Solo Male", "4805"),
|
}.let(screen::addPreference)
|
||||||
SearchPair("Big Breasts", "2528"),
|
}
|
||||||
SearchPair("Nakadashi", "2418"),
|
|
||||||
SearchPair("Blowjob", "64"),
|
|
||||||
SearchPair("Schoolgirl Uniform", "2522"),
|
|
||||||
SearchPair("Stockings", "33")
|
|
||||||
)
|
|
||||||
|
|
||||||
private fun getSeriesList() = listOf(
|
private inline val blacklist: String
|
||||||
SearchPair("Original Work", "1093"),
|
get() = preferences.getString("blacklist", "")!!
|
||||||
SearchPair("Kantai Collection", "1316"),
|
|
||||||
SearchPair("Touhou", "747"),
|
private inline fun <reified T> Response.decode() =
|
||||||
SearchPair("Fate Grand Order", "2171"),
|
json.decodeFromString<T>(body!!.string())
|
||||||
SearchPair("Idolmaster", "306"),
|
|
||||||
SearchPair("Granblue Fantasy", "2041"),
|
override fun imageUrlParse(response: Response) =
|
||||||
SearchPair("Girls Und Panzer", "1324")
|
throw UnsupportedOperationException("Not used")
|
||||||
)
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
private val dateFormat =
|
||||||
@SuppressLint("SimpleDateFormat")
|
SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS", Locale.ROOT)
|
||||||
private val DATE_FORMAT = SimpleDateFormat("dd.MM.yyyy")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
package eu.kanade.tachiyomi.extension.all.simplyhentai
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class SHList<T>(val pagination: SHPagination, val data: List<T>)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class SHPagination(val next: Int?)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class SHWrapper(val `object`: SHObject)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class SHObject(
|
||||||
|
val preview: SHImage,
|
||||||
|
val series: SHTag,
|
||||||
|
val slug: String,
|
||||||
|
val title: String
|
||||||
|
) {
|
||||||
|
val path by lazy { "/${series.slug}/$slug" }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class SHImage(val page_num: Int, val sizes: SHSizes)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class SHSizes(val full: String, val thumb: String)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class SHTag(val slug: String, val title: String)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class SHAlbum(val data: SHData)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class SHData(
|
||||||
|
val artists: List<SHTag>,
|
||||||
|
val characters: List<SHTag>,
|
||||||
|
val created_at: String,
|
||||||
|
val description: String?,
|
||||||
|
val images: List<SHImage>,
|
||||||
|
val preview: SHImage,
|
||||||
|
val series: SHTag,
|
||||||
|
val slug: String,
|
||||||
|
val tags: List<SHTag>,
|
||||||
|
val title: String,
|
||||||
|
val translators: List<SHTag>
|
||||||
|
) {
|
||||||
|
val path by lazy { "/${series.slug}/$slug" }
|
||||||
|
}
|
|
@ -1,34 +1,26 @@
|
||||||
package eu.kanade.tachiyomi.extension.all.simplyhentai
|
package eu.kanade.tachiyomi.extension.all.simplyhentai
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.annotations.Nsfw
|
import eu.kanade.tachiyomi.annotations.Nsfw
|
||||||
import eu.kanade.tachiyomi.source.Source
|
|
||||||
import eu.kanade.tachiyomi.source.SourceFactory
|
import eu.kanade.tachiyomi.source.SourceFactory
|
||||||
|
import kotlinx.serialization.ExperimentalSerializationApi
|
||||||
|
|
||||||
@Nsfw
|
@ExperimentalSerializationApi @Nsfw
|
||||||
class SimplyHentaiFactory : SourceFactory {
|
class SimplyHentaiFactory : SourceFactory {
|
||||||
override fun createSources(): List<Source> = listOf(
|
override fun createSources() = listOf(
|
||||||
SimplyHentaiEN(),
|
SimplyHentai("en"),
|
||||||
SimplyHentaiJA(),
|
SimplyHentai("ja"),
|
||||||
SimplyHentaiZH(),
|
SimplyHentai("zh"),
|
||||||
SimplyHentaiKO(),
|
SimplyHentai("ko"),
|
||||||
SimplyHentaiES(),
|
SimplyHentai("es"),
|
||||||
SimplyHentaiRU(),
|
SimplyHentai("ru"),
|
||||||
SimplyHentaiFR(),
|
SimplyHentai("fr"),
|
||||||
SimplyHentaiDE(),
|
SimplyHentai("de"),
|
||||||
SimplyHentaiPT()
|
object : SimplyHentai("pt-BR") {
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
class SimplyHentaiEN : SimplyHentai("en", "english", "1")
|
|
||||||
class SimplyHentaiJA : SimplyHentai("ja", "japanese", "2")
|
|
||||||
class SimplyHentaiZH : SimplyHentai("zh", "chinese", "11")
|
|
||||||
class SimplyHentaiKO : SimplyHentai("ko", "korean", "9")
|
|
||||||
class SimplyHentaiES : SimplyHentai("es", "spanish", "8")
|
|
||||||
class SimplyHentaiRU : SimplyHentai("ru", "russian", "7")
|
|
||||||
class SimplyHentaiFR : SimplyHentai("fr", "french", "3")
|
|
||||||
class SimplyHentaiDE : SimplyHentai("de", "german", "4")
|
|
||||||
class SimplyHentaiPT : SimplyHentai("pt-BR", "portuguese", "6") {
|
|
||||||
// The site uses a Portugal flag for the language,
|
// The site uses a Portugal flag for the language,
|
||||||
// but the contents are in Brazilian Portuguese.
|
// but the contents are in Brazilian Portuguese.
|
||||||
override val id: Long = 7265793330155215502
|
override val id = 23032005200449651
|
||||||
|
},
|
||||||
|
SimplyHentai("it"),
|
||||||
|
SimplyHentai("pl"),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
package eu.kanade.tachiyomi.extension.all.simplyhentai
|
||||||
|
|
||||||
|
import eu.kanade.tachiyomi.source.model.Filter
|
||||||
|
|
||||||
|
class SortFilter(values: Array<String> = labels) : Filter.Select<String>("Sort by", values) {
|
||||||
|
val orders = arrayOf("", "upload-date", "popularity")
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val labels = arrayOf("Relevance", "Upload Date", "Popularity")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SeriesFilter : Filter.Text("Series") {
|
||||||
|
inline val value: String?
|
||||||
|
get() = state.ifBlank { null }
|
||||||
|
}
|
||||||
|
|
||||||
|
class TagsFilter : Filter.Text("Tags") {
|
||||||
|
inline val value: List<String>?
|
||||||
|
get() = if (state.isBlank()) null else state.split(',')
|
||||||
|
}
|
||||||
|
|
||||||
|
class ArtistsFilter : Filter.Text("Artists") {
|
||||||
|
inline val value: List<String>?
|
||||||
|
get() = if (state.isBlank()) null else state.split(',')
|
||||||
|
}
|
||||||
|
|
||||||
|
class TranslatorsFilter : Filter.Text("Translators") {
|
||||||
|
inline val value: List<String>?
|
||||||
|
get() = if (state.isBlank()) null else state.split(',')
|
||||||
|
}
|
||||||
|
|
||||||
|
class CharactersFilter : Filter.Text("Characters") {
|
||||||
|
inline val value: List<String>?
|
||||||
|
get() = if (state.isBlank()) null else state.split(',')
|
||||||
|
}
|
||||||
|
|
||||||
|
class Note(type: String) : Filter.Header("Separate multiple $type with commas (,)")
|
Loading…
Reference in New Issue