hanime: add filters and video quality setting
Co-authored-by: Modder4869 <Modder4869@users.noreply.github.com>
This commit is contained in:
@ -5,7 +5,7 @@ ext {
|
||||
extName = 'hanime.tv'
|
||||
pkgNameSuffix = 'en.hanime'
|
||||
extClass = '.Hanime'
|
||||
extVersionCode = 9
|
||||
extVersionCode = 10
|
||||
libVersion = '12'
|
||||
containsNsfw = true
|
||||
}
|
||||
|
@ -1,9 +1,15 @@
|
||||
package eu.kanade.tachiyomi.animeextension.en.hanime
|
||||
|
||||
import android.app.Application
|
||||
import android.content.SharedPreferences
|
||||
import androidx.preference.ListPreference
|
||||
import androidx.preference.PreferenceScreen
|
||||
import com.google.gson.JsonArray
|
||||
import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonObject
|
||||
import com.google.gson.JsonParser
|
||||
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
|
||||
import eu.kanade.tachiyomi.animesource.model.AnimeFilter
|
||||
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
|
||||
import eu.kanade.tachiyomi.animesource.model.AnimesPage
|
||||
import eu.kanade.tachiyomi.animesource.model.SAnime
|
||||
@ -18,8 +24,11 @@ import okhttp3.Request
|
||||
import okhttp3.RequestBody
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import okhttp3.Response
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import java.util.Locale
|
||||
|
||||
class Hanime : AnimeHttpSource() {
|
||||
class Hanime : ConfigurableAnimeSource, AnimeHttpSource() {
|
||||
|
||||
override val name = "hanime.tv"
|
||||
|
||||
@ -29,26 +38,25 @@ class Hanime : AnimeHttpSource() {
|
||||
|
||||
override val supportsLatest = true
|
||||
|
||||
private val preferences: SharedPreferences by lazy {
|
||||
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
|
||||
}
|
||||
|
||||
private fun searchRequestBody(query: String, page: Int, filters: AnimeFilterList): RequestBody {
|
||||
var filterString = ""
|
||||
for (filter in filters.list) {
|
||||
filterString += if (filters.lastIndexOf(filter) == filters.lastIndex) {
|
||||
"\"${filter.name}\""
|
||||
} else {
|
||||
"\"${filter.name}\","
|
||||
}
|
||||
}
|
||||
val (includedTags, blackListedTags, brands, tagsMode, orderBy, ordering) = getSearchParameters(filters)
|
||||
|
||||
return """
|
||||
{"search_text": "$query",
|
||||
"tags": [$filterString],
|
||||
"tags_mode":"AND",
|
||||
"brands": [],
|
||||
"blacklist": [],
|
||||
"order_by": "likes",
|
||||
"ordering": "desc",
|
||||
"tags": $includedTags,
|
||||
"tags_mode":"$tagsMode",
|
||||
"brands": $brands,
|
||||
"blacklist": $blackListedTags,
|
||||
"order_by": "$orderBy",
|
||||
"ordering": "$ordering",
|
||||
"page": ${page - 1}}
|
||||
""".trimIndent().toRequestBody("application/json".toMediaType())
|
||||
}
|
||||
|
||||
private val popularRequestHeaders =
|
||||
Headers.headersOf("authority", "search.htv-services.com", "accept", "application/json, text/plain, */*", "content-type", "application/json;charset=UTF-8")
|
||||
|
||||
@ -132,6 +140,24 @@ class Hanime : AnimeHttpSource() {
|
||||
return linkList
|
||||
}
|
||||
|
||||
override fun List<Video>.sort(): List<Video> {
|
||||
val quality = preferences.getString("preferred_quality", null)
|
||||
if (quality != null) {
|
||||
val newList = mutableListOf<Video>()
|
||||
var preferred = 0
|
||||
for (video in this) {
|
||||
if (video.quality.contains(quality)) {
|
||||
newList.add(preferred, video)
|
||||
preferred++
|
||||
} else {
|
||||
newList.add(video)
|
||||
}
|
||||
}
|
||||
return newList
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
override fun episodeListRequest(anime: SAnime): Request {
|
||||
val slug = anime.url.substringAfterLast("/")
|
||||
return GET("https://hw.hanime.tv/api/v8/video?id=$slug")
|
||||
@ -168,4 +194,338 @@ class Hanime : AnimeHttpSource() {
|
||||
val responseString = response.body!!.string()
|
||||
return parseSearchJson(responseString)
|
||||
}
|
||||
// Filters
|
||||
override fun getFilterList(): AnimeFilterList = AnimeFilterList(
|
||||
TagList(getTags()),
|
||||
BrandList(getBrands()),
|
||||
SortFilter(sortableList.map { it.first }.toTypedArray()),
|
||||
TagInclusionMode()
|
||||
|
||||
)
|
||||
internal class Tag(val id: String, name: String) : AnimeFilter.TriState(name)
|
||||
internal class Brand(val id: String, name: String) : AnimeFilter.CheckBox(name)
|
||||
private class TagList(tags: List<Tag>) : AnimeFilter.Group<Tag>("Tags", tags)
|
||||
private class BrandList(brands: List<Brand>) : AnimeFilter.Group<Brand>("Brands", brands)
|
||||
private class TagInclusionMode :
|
||||
AnimeFilter.Select<String>("Included tags mode", arrayOf("And", "Or"), 0)
|
||||
|
||||
data class SearchParameters(
|
||||
val includedTags: ArrayList<String>,
|
||||
val blackListedTags: ArrayList<String>,
|
||||
val brands: ArrayList<String>,
|
||||
val tagsMode: String,
|
||||
val orderBy: String,
|
||||
val ordering: String
|
||||
)
|
||||
|
||||
private fun getSearchParameters(filters: AnimeFilterList): SearchParameters {
|
||||
val includedTags = ArrayList<String>()
|
||||
val blackListedTags = ArrayList<String>()
|
||||
val brands = ArrayList<String>()
|
||||
var tagsMode = "AND"
|
||||
var orderBy = "likes"
|
||||
var ordering = "desc"
|
||||
filters.forEach { filter ->
|
||||
when (filter) {
|
||||
is TagList -> {
|
||||
filter.state.forEach { tag ->
|
||||
if (tag.isIncluded()) {
|
||||
includedTags.add(
|
||||
"\"" + tag.id.toLowerCase(
|
||||
Locale.US
|
||||
) + "\""
|
||||
)
|
||||
} else if (tag.isExcluded()) {
|
||||
blackListedTags.add(
|
||||
"\"" + tag.id.toLowerCase(
|
||||
Locale.US
|
||||
) + "\""
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
is TagInclusionMode -> {
|
||||
tagsMode = filter.values[filter.state].toUpperCase(Locale.US)
|
||||
}
|
||||
is SortFilter -> {
|
||||
if (filter.state != null) {
|
||||
val query = sortableList[filter.state!!.index].second
|
||||
val value = when (filter.state!!.ascending) {
|
||||
true -> "asc"
|
||||
false -> "desc"
|
||||
}
|
||||
ordering = value
|
||||
orderBy = query
|
||||
}
|
||||
}
|
||||
is BrandList -> {
|
||||
filter.state.forEach { brand ->
|
||||
if (brand.state) {
|
||||
brands.add(
|
||||
"\"" + brand.id.toLowerCase(
|
||||
Locale.US
|
||||
) + "\""
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
return SearchParameters(includedTags, blackListedTags, brands, tagsMode, orderBy, ordering)
|
||||
}
|
||||
|
||||
private fun getBrands() = listOf(
|
||||
Brand("37c-Binetsu", "37c-binetsu"),
|
||||
Brand("Adult Source Media", "adult-source-media"),
|
||||
Brand("Ajia-Do", "ajia-do"),
|
||||
Brand("Almond Collective", "almond-collective"),
|
||||
Brand("Alpha Polis", "alpha-polis"),
|
||||
Brand("Ameliatie", "ameliatie"),
|
||||
Brand("Amour", "amour"),
|
||||
Brand("Animac", "animac"),
|
||||
Brand("Antechinus", "antechinus"),
|
||||
Brand("APPP", "appp"),
|
||||
Brand("Arms", "arms"),
|
||||
Brand("Bishop", "bishop"),
|
||||
Brand("Blue Eyes", "blue-eyes"),
|
||||
Brand("BOMB! CUTE! BOMB!", "bomb-cute-bomb"),
|
||||
Brand("Bootleg", "bootleg"),
|
||||
Brand("BreakBottle", "breakbottle"),
|
||||
Brand("BugBug", "bugbug"),
|
||||
Brand("Bunnywalker", "bunnywalker"),
|
||||
Brand("Celeb", "celeb"),
|
||||
Brand("Central Park Media", "central-park-media"),
|
||||
Brand("ChiChinoya", "chichinoya"),
|
||||
Brand("Chocolat", "chocolat"),
|
||||
Brand("ChuChu", "chuchu"),
|
||||
Brand("Circle Tribute", "circle-tribute"),
|
||||
Brand("CoCoans", "cocoans"),
|
||||
Brand("Collaboration Works", "collaboration-works"),
|
||||
Brand("Comet", "comet"),
|
||||
Brand("Comic Media", "comic-media"),
|
||||
Brand("Cosmos", "cosmos"),
|
||||
Brand("Cranberry", "cranberry"),
|
||||
Brand("Crimson", "crimson"),
|
||||
Brand("D3", "d3"),
|
||||
Brand("Daiei", "daiei"),
|
||||
Brand("demodemon", "demodemon"),
|
||||
Brand("Digital Works", "digital-works"),
|
||||
Brand("Discovery", "discovery"),
|
||||
Brand("Dollhouse", "dollhouse"),
|
||||
Brand("EBIMARU-DO", "ebimaru-do"),
|
||||
Brand("Echo", "echo"),
|
||||
Brand("ECOLONUN", "ecolonun"),
|
||||
Brand("Edge", "edge"),
|
||||
Brand("Erozuki", "erozuki"),
|
||||
Brand("evee", "evee"),
|
||||
Brand("FINAL FUCK 7", "final-fuck-7"),
|
||||
Brand("Five Ways", "five-ways"),
|
||||
Brand("Friends Media Station", "friends-media-station"),
|
||||
Brand("Front Line", "front-line"),
|
||||
Brand("fruit", "fruit"),
|
||||
Brand("Godoy", "godoy"),
|
||||
Brand("GOLD BEAR", "gold-bear"),
|
||||
Brand("gomasioken", "gomasioken"),
|
||||
Brand("Green Bunny", "green-bunny"),
|
||||
Brand("Groover", "groover"),
|
||||
Brand("Hoods Entertainment", "hoods-entertainment"),
|
||||
Brand("Hot Bear", "hot-bear"),
|
||||
Brand("Hykobo", "hykobo"),
|
||||
Brand("IRONBELL", "ironbell"),
|
||||
Brand("Ivory Tower", "ivory-tower"),
|
||||
Brand("J.C.", "j-c"),
|
||||
Brand("Jellyfish", "jellyfish"),
|
||||
Brand("Jewel", "jewel"),
|
||||
Brand("Jumondo", "jumondo"),
|
||||
Brand("kate_sai", "kate_sai"),
|
||||
Brand("KENZsoft", "kenzsoft"),
|
||||
Brand("King Bee", "king-bee"),
|
||||
Brand("Kitty Media", "kitty-media"),
|
||||
Brand("Knack", "knack"),
|
||||
Brand("Kuril", "kuril"),
|
||||
Brand("L.", "l"),
|
||||
Brand("Lemon Heart", "lemon-heart"),
|
||||
Brand("Lilix", "lilix"),
|
||||
Brand("Lune Pictures", "lune-pictures"),
|
||||
Brand("Magic Bus", "magic-bus"),
|
||||
Brand("Magin Label", "magin-label"),
|
||||
Brand("Majin Petit", "majin-petit"),
|
||||
Brand("Marigold", "marigold"),
|
||||
Brand("Mary Jane", "mary-jane"),
|
||||
Brand("MediaBank", "mediabank"),
|
||||
Brand("Media Blasters", "media-blasters"),
|
||||
Brand("Metro Notes", "metro-notes"),
|
||||
Brand("Milky", "milky"),
|
||||
Brand("MiMiA Cute", "mimia-cute"),
|
||||
Brand("Moon Rock", "moon-rock"),
|
||||
Brand("Moonstone Cherry", "moonstone-cherry"),
|
||||
Brand("Mousou Senka", "mousou-senka"),
|
||||
Brand("MS Pictures", "ms-pictures"),
|
||||
Brand("Muse", "muse"),
|
||||
Brand("N43", "n43"),
|
||||
Brand("Nihikime no Dozeu", "nihikime-no-dozeu"),
|
||||
Brand("Nikkatsu Video", "nikkatsu-video"),
|
||||
Brand("nur", "nur"),
|
||||
Brand("NuTech Digital", "nutech-digital"),
|
||||
Brand("Obtain Future", "obtain-future"),
|
||||
Brand("Otodeli", "otodeli"),
|
||||
Brand("@ OZ", "oz"),
|
||||
Brand("Pashmina", "pashmina"),
|
||||
Brand("Passione", "passione"),
|
||||
Brand("Peach Pie", "peach-pie"),
|
||||
Brand("Pinkbell", "pinkbell"),
|
||||
Brand("Pink Pineapple", "pink-pineapple"),
|
||||
Brand("Pix", "pix"),
|
||||
Brand("Pixy Soft", "pixy-soft"),
|
||||
Brand("Pocomo Premium", "pocomo-premium"),
|
||||
Brand("PoRO", "poro"),
|
||||
Brand("Project No.9", "project-no-9"),
|
||||
Brand("Pumpkin Pie", "pumpkin-pie"),
|
||||
Brand("Queen Bee", "queen-bee"),
|
||||
Brand("Rabbit Gate", "rabbit-gate"),
|
||||
Brand("sakamotoJ", "sakamotoj"),
|
||||
Brand("Sakura Purin", "sakura-purin"),
|
||||
Brand("SANDWICHWORKS", "sandwichworks"),
|
||||
Brand("Schoolzone", "schoolzone"),
|
||||
Brand("seismic", "seismic"),
|
||||
Brand("SELFISH", "selfish"),
|
||||
Brand("Seven", "seven"),
|
||||
Brand("Shadow Prod. Co.", "shadow-prod-co"),
|
||||
Brand("Shelf", "shelf"),
|
||||
Brand("Shinyusha", "shinyusha"),
|
||||
Brand("ShoSai", "shosai"),
|
||||
Brand("Showten", "showten"),
|
||||
Brand("SoftCell", "softcell"),
|
||||
Brand("Soft on Demand", "soft-on-demand"),
|
||||
Brand("SPEED", "speed"),
|
||||
Brand("STARGATE3D", "stargate3d"),
|
||||
Brand("Studio 9 Maiami", "studio-9-maiami"),
|
||||
Brand("Studio Akai Shohosen", "studio-akai-shohosen"),
|
||||
Brand("Studio Deen", "studio-deen"),
|
||||
Brand("Studio Fantasia", "studio-fantasia"),
|
||||
Brand("Studio FOW", "studio-fow"),
|
||||
Brand("studio GGB", "studio-ggb"),
|
||||
Brand("Studio Houkiboshi", "studio-houkiboshi"),
|
||||
Brand("Studio Zealot", "studio-zealot"),
|
||||
Brand("Suiseisha", "suiseisha"),
|
||||
Brand("Suzuki Mirano", "suzuki-mirano"),
|
||||
Brand("SYLD", "syld"),
|
||||
Brand("TDK Core", "tdk-core"),
|
||||
Brand("t japan", "t-japan"),
|
||||
Brand("TNK", "tnk"),
|
||||
Brand("TOHO", "toho"),
|
||||
Brand("Toranoana", "toranoana"),
|
||||
Brand("T-Rex", "t-rex"),
|
||||
Brand("Triangle", "triangle"),
|
||||
Brand("Trimax", "trimax"),
|
||||
Brand("TYS Work", "tys-work"),
|
||||
Brand("U-Jin", "u-jin"),
|
||||
Brand("Umemaro-3D", "umemaro-3d"),
|
||||
Brand("Union Cho", "union-cho"),
|
||||
Brand("Valkyria", "valkyria"),
|
||||
Brand("Vanilla", "vanilla"),
|
||||
Brand("White Bear", "white-bear"),
|
||||
Brand("X City", "x-city"),
|
||||
Brand("yosino", "yosino"),
|
||||
Brand("Y.O.U.C.", "y-o-u-c"),
|
||||
Brand("ZIZ", "ziz")
|
||||
)
|
||||
|
||||
private fun getTags() = listOf(
|
||||
Tag("3D", "3D"),
|
||||
Tag("AHEGAO", "AHEGAO"),
|
||||
Tag("ANAL", "ANAL"),
|
||||
Tag("BDSM", "BDSM"),
|
||||
Tag("BIG BOOBS", "BIG BOOBS"),
|
||||
Tag("BLOW JOB", "BLOW JOB"),
|
||||
Tag("BONDAGE", "BONDAGE"),
|
||||
Tag("BOOB JOB", "BOOB JOB"),
|
||||
Tag("CENSORED", "CENSORED"),
|
||||
Tag("COMEDY", "COMEDY"),
|
||||
Tag("COSPLAY", "COSPLAY"),
|
||||
Tag("CREAMPIE", "CREAMPIE"),
|
||||
Tag("DARK SKIN", "DARK SKIN"),
|
||||
Tag("FACIAL", "FACIAL"),
|
||||
Tag("FANTASY", "FANTASY"),
|
||||
Tag("FILMED", "FILMED"),
|
||||
Tag("FOOT JOB", "FOOT JOB"),
|
||||
Tag("FUTANARI", "FUTANARI"),
|
||||
Tag("GANGBANG", "GANGBANG"),
|
||||
Tag("GLASSES", "GLASSES"),
|
||||
Tag("HAND JOB", "HAND JOB"),
|
||||
Tag("HAREM", "HAREM"),
|
||||
Tag("HD", "HD"),
|
||||
Tag("HORROR", "HORROR"),
|
||||
Tag("INCEST", "INCEST"),
|
||||
Tag("INFLATION", "INFLATION"),
|
||||
Tag("LACTATION", "LACTATION"),
|
||||
Tag("LOLI", "LOLI"),
|
||||
Tag("MAID", "MAID"),
|
||||
Tag("MASTURBATION", "MASTURBATION"),
|
||||
Tag("MILF", "MILF"),
|
||||
Tag("MIND BREAK", "MIND BREAK"),
|
||||
Tag("MIND CONTROL", "MIND CONTROL"),
|
||||
Tag("MONSTER", "MONSTER"),
|
||||
Tag("NEKOMIMI", "NEKOMIMI"),
|
||||
Tag("NTR", "NTR"),
|
||||
Tag("NURSE", "NURSE"),
|
||||
Tag("ORGY", "ORGY"),
|
||||
Tag("PLOT", "PLOT"),
|
||||
Tag("POV", "POV"),
|
||||
Tag("PREGNANT", "PREGNANT"),
|
||||
Tag("PUBLIC SEX", "PUBLIC SEX"),
|
||||
Tag("RAPE", "RAPE"),
|
||||
Tag("REVERSE RAPE", "REVERSE RAPE"),
|
||||
Tag("RIMJOB", "RIMJOB"),
|
||||
Tag("SCAT", "SCAT"),
|
||||
Tag("SCHOOL GIRL", "SCHOOL GIRL"),
|
||||
Tag("SHOTA", "SHOTA"),
|
||||
Tag("SOFTCORE", "SOFTCORE"),
|
||||
Tag("SWIMSUIT", "SWIMSUIT"),
|
||||
Tag("TEACHER", "TEACHER"),
|
||||
Tag("TENTACLE", "TENTACLE"),
|
||||
Tag("THREESOME", "THREESOME"),
|
||||
Tag("TOYS", "TOYS"),
|
||||
Tag("TRAP", "TRAP"),
|
||||
Tag("TSUNDERE", "TSUNDERE"),
|
||||
Tag("UGLY BASTARD", "UGLY BASTARD"),
|
||||
Tag("UNCENSORED", "UNCENSORED"),
|
||||
Tag("VANILLA", "VANILLA"),
|
||||
Tag("VIRGIN", "VIRGIN"),
|
||||
Tag("WATERSPORTS", "WATERSPORTS"),
|
||||
Tag("X-RAY", "X-RAY"),
|
||||
Tag("YAOI", "YAOI"),
|
||||
Tag("YURI", "YURI"),
|
||||
)
|
||||
|
||||
private val sortableList = listOf(
|
||||
Pair("Uploads", "created_at_unix"),
|
||||
Pair("Views", "views"),
|
||||
Pair("Likes", "likes"),
|
||||
Pair("Release", "released_at_unix"),
|
||||
Pair("Alphabetical", "title_sortable"),
|
||||
)
|
||||
|
||||
class SortFilter(sortables: Array<String>) : AnimeFilter.Sort("Sort", sortables, Selection(2, false))
|
||||
|
||||
// Preferences
|
||||
override fun setupPreferenceScreen(screen: PreferenceScreen) {
|
||||
val videoQualityPref = ListPreference(screen.context).apply {
|
||||
key = "preferred_quality"
|
||||
title = "Preferred quality"
|
||||
entries = arrayOf("720p", "480p", "360p")
|
||||
entryValues = arrayOf("720", "480", "360")
|
||||
setDefaultValue("720")
|
||||
summary = "%s"
|
||||
|
||||
setOnPreferenceChangeListener { _, newValue ->
|
||||
val selected = newValue as String
|
||||
val index = findIndexOfValue(selected)
|
||||
val entry = entryValues[index] as String
|
||||
preferences.edit().putString(key, entry).commit()
|
||||
}
|
||||
}
|
||||
screen.addPreference(videoQualityPref)
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user