8XAlXgZF54wwmIz$@hT1`@g6s{O)S--7;?W
zBc@_xsYlIIa;0WV8w}wU!?`%4O^G-|uBJUyce4mJ+`y1&-wpE3FL#M|BXYhwgJj^L
zLhpgM#|w|MpN!{^6Q34%`!YaprlB!ubR0f!%ElXkL&UJr*FIrvwql_eOr|Wxd}Cfl
zAs(>_X7Y1|C%cH-{@Hi16SW%AW9C~OyjuG54q-}FYF4|ceg_JRjXidR`V;AP`%~v`
zB;P!lRK9mz7N4M*xgVG!l1`{)iEJ)k5a|8o0g_}yf+7^tWq
zdgQwpKtGp%bS|;1pmo_0MtjZsdql~B&&~E77#=pm#^z9DJTYMO?G?WAM528~==*IH
zB#mD2S81da>*RnDlx>XzcH8p}nQNNTXjQ9s`nn
zVbaz*4frclW*=<+1woUIG%0zD@e@+k{)FJ)hv`mkr@QhNA8Rgs)uNM|Sk>R6C{h&3pdBqadc=ED#`z{5y=Hsl
zvhgA+!Af6Pd?=8MYYQc$Wy3QtF6}<27KovpgM%p(u~7l#DFavI{Ni_
z$O)>m2|?xP*f5<`A&)=?RpV2?zC_A+^ja5JoK%N-
zTJnF$ON~RcUjX&j+3qc4)dySyS3s5es_O3Vq@DcbA)>BO3n1KMpA=mIt8Ss0+co*0
zq5Y(n*nXi6&d8C2<5pWI2c}G^-P>c&+`@BXarc8A{C=k^(H~Hy())E9f95bnYM!E*
zfPcMZCbtiSlj=*Q!+#VjX;aeKnS@O*$br_nifeD@#Ma@vBxf%njCngj4{l7AbZ_V_
zItuA7{D^^ccy(leUe%hxAkj3O2;5HY%cZ7{;P0-x{VRiJpS>n&x@3?4VuOv^S+FO(ALd;|Mu&qYj!!SH??ci(JA98N
z8}vPJlSChQI`+Bj59G(yh`@stJWdW9=tc!1Tgp8*%OZ1Cc_l)j80F8co^atAD0Hrm
zF9LyYTWVJ`Y3vwr<6Uo+OYwPn1C$8GuRB2h9)HfJ)xix^w*n47<+XCW9hZS>9S)?P
z9{<1m!Runv<4Ap_{%`Bvq{bUPNdsHKL`0_yaATi&`I;@qsK`PN^z$9JwzJ)Xaf~?(
zje<&*xuAt_M;8On7vJLxu%+YSD$O91gYOJ0+kN3v=lZ*omPYxuoDDxrN^mPgCL4u2
zBc+~`xPq2{Pk|^h6#Y_KBXF8)AB_E#cKE_Vc$0>EY&tF{KLtnHf)4(sSdfVPme7@p
zs#Y#08TrIc@Kd~YOZ&lb7{sjCbQud>BXr7TgpTRC;S0aB0~h?
zzyY-`8o5VC>j4YzJ6GEax3l;#8JH1{zo)88Ka@DW>guRmEibK@eZg~Ay-m85Y`*ts(gm|AseJbP=lV%ryr*WNO$)
zV8-Jk1)jL{dp57N?o*IX%s)JLp=Q7+=eZ&KY+sw>36gtF
z55FeN7*Ta$YHqz|Q4@v$Y6?jzqp3I)Zmr_>WA{8HA2c2DsuByWj+TRPbUaa21;D8>
zGudv8!`KswexM{e>^C4KxP*|&)n1aa-;wTH3Du}&YF9nAJq>3$?vDueA_fAdCRfE4
zgG_Kyn{~3vM&Hei>{GxZ?!6yzJXK0HyKWOfP)xccW6aPc?==#T4RI^jR8uwmoTslJ>+94XbSijm<(Kov_#IMTd8iW4%1X1EcSf0{{p%4(xU=8LYH%3
zv}mP;;m_}pDiBa+xvllTVk8>W_DcK|tdF!}ZltpJopAOYcBO5}5za}+<`4fRR{0ZH
zuBjj1PXAWCfr04)E$b^Ziy}9kZm;iBv%L{>cpu5ED2@UPd%-UgW;sy_+jb#lY_ymxHOOYnru8;PhP0*Sfe#O
zq-4b^v-==FJ>AK2&`qOuTtxHtCDOyRzX+gQYWV~Se9!yT>^0KR@}i|G6Srq-Z*s~!uOq3Ih!j=ezYD#EoFKw
zUvOY&V_`$FkH?c6s!X}D4nWP+%=K17w6m*yC3$5&HW-x;9HfPN-2
zGbtq^9ohN4^};dJ;$Ibz|EBPCqM6jc9ITLVQ#xP75~?C!GPaKG={3A;`#%x34Mf
zO0y?stZWn;`1JHNdCSzearEVEYhiABgb!OuR=7&5Vz}8%{sTnp3;H=#^pq9;(S855
zk~2n?NLzZg{(BDp;t~B~@cH|U0Z)V;)t*n95`UtUni{zQ-@tg%z&%Hj$R*2Tx`fua
z$TNgF`Fi~paIJDw@RRvpASC!MQPwku%K@O&*q<+eTE!W>z)&Bpz?%lPeMaW(Ye~30
z?Pv;^kXR5A2q6zUR+sGaqqyjl+HU79e+GwLr+*uszPLR&Oyf
zEhO&+er5IC(odek<>8?x^Ka&VeP!$sn)kDbFsDraNWoT9@tib7rCYzNPBGL*
z|DK;&tYk*;`^jiJP)AX|!q(v&zX6U1NGdP!oB+G+Fb(c@j;cR7SkBeT1hXrLb2K+^
zyziddli(E>R&z}s9%SpQeL`K>vW|WXqicu|sw2#@s>B!YkeEr*FvZYQlL&Z*--*y;
zHfaCJ7ByE!bt)r{9z`ofhTZ58=(;EP_!%;1z-ClAA;Q)J!pf~N>?W(&C=^WN_QH(F
zwR5-!qITq!H8$c4xh#5gBzf3q4$NT2=jO@36lK7Slz3UD%7bXI_@YMvBpcT6=Vc#O
z2{6;pEuzWfi5XS-TJj?0*?*n)m9n}{$(K0$13JJ7e;eSDyDJ1uDqe`2&;lhn%>%KO
zbY{V^wA|g6<8Z0BZjKuz*Ejkd&VdQH?9OJVQAVEL`6I5>*<3pIL9dyW?%`lQ_fU76
zOS~bH5a^M3hEj#J%w06;QiPf}92=nk`Foot4>k?Gx0w0N5Rm)hq#eVG7}^{q7Pg+7
zf`t+mw|TN^g@8wGoHEn5(NPv}c4ZW$qXzdlRNBZLdP*9NOjUr6;%--@X10@$h1t_C>{)7KWXIm1IJ!vUPdK)qOv~C^FN@P
z`Kb*z>mMMy-@#;?8iEZW>9Of=m1OTYcDfmO$wRkK(?9kY7aQE&HYHt)HA&*qE!pa4
zV)t;Y9*f|v@G41GuB5d4U_0Sp7_HpM8#F#T#y9vaD8QVzt%N;2CB{KH+CYImHj5H>
zE#D1>NG)ls)bVASTNSsKH5Q|S&71qmNo&W(Q=0nFUBu}VFMH@(Qi`9Nx`{SJ&c`<}
zz%#k2(h`KEXSM<~asbFH4q>0v>>SS~9p&g+X)uTFD;sL~RTX%*5j;VX8QVm#Wmr!X
zeh`o{TD_}xxs|GT0wO>FEODxxqVXL&C5jRM8{nF+jv~X@K+qeuxCJ+`ACQKF
zb0&qm9LY2tY#KZwmO>LZg#p5rB#dO@>2d_40^6DNIoPyc44)#Tk6py&;P>8Snm8gN
zS!3S=KKD18x(s3YHc+%fEZNVTfW;_5)3HCHaeX_M|9Bs(x%|*T($L35tU|B^GSlWU
z<|sZoqX04L&j!LZwdU=jW8do<5ocHPlo_=tDs;7@(gD29Cu4WM*h)<50~PnUUQ#>r
z77Pq=S?JNs7%~Ei0c`2pCLF`j={j?$^T-#UvT>ZuhnnKU9?{;!Wxf7NTrHv)Fl8SN
z1vQ!e!a3K`endhL(6Zs;c&4$ZGhz++-X;U?kD%2qf#v)e)PBo-nHR>}F2~k$tA;4M|S0*|Lvm+?LhUE5(*90@r^0@xk8G6YKp=lJx&){5VGR>D#sA=1@
zZi;M1oh>~KL1;g^o~G|)&)LU6OX|2wVd`NB(I6`G&k#oA|KB;U
z|4+1E>agEqc#>t^^5ry_>tmV!U7aNl?T7aK$1@(Z!MXH6ej%_OTwVC@ZaoaK9JG$`
za%rwq_UomvoBY6=@ds=m>8fip?SgHvnj_QzG@HgA3<<(HLZY$bBv0-(r(9SP?6(XJ
zc|=&KFkhkv4A(PrrRrZViTo&m7`_PNNMUZ{K?%G9*ii7jaE{Q{uw`r=ZA7!DKM-~z
zkZ$>rpuE?`u4>=8&m92w1yKA*!q5kyoZ{4x^gOxm#teOHg)T7Uu)K%~5UweM_hE=e-_<*Ge^I8}vcsYF
z2f>}zQ%29z(#+FJ(8As7LqKtHaB;D6am}Z}5i#!~dGW)6U7t!_&;k_5alQEXc+EKPSnJ{
literal 0
HcmV?d00001
diff --git a/src/pt/sukianimes/src/eu/kanade/tachiyomi/animeextension/pt/sukianimes/SKFilters.kt b/src/pt/sukianimes/src/eu/kanade/tachiyomi/animeextension/pt/sukianimes/SKFilters.kt
new file mode 100644
index 000000000..818374a09
--- /dev/null
+++ b/src/pt/sukianimes/src/eu/kanade/tachiyomi/animeextension/pt/sukianimes/SKFilters.kt
@@ -0,0 +1,170 @@
+package eu.kanade.tachiyomi.animeextension.pt.sukianimes
+
+import eu.kanade.tachiyomi.animesource.model.AnimeFilter
+import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
+
+object SKFilters {
+
+ open class QueryPartFilter(
+ displayName: String,
+ val vals: Array>
+ ) : AnimeFilter.Select(
+ displayName,
+ vals.map { it.first }.toTypedArray()
+ ) {
+
+ fun toQueryPart() = vals[state].second
+ }
+
+ private class CheckBoxVal(name: String, state: Boolean = false) : AnimeFilter.CheckBox(name, state)
+ open class CheckBoxFilterList(name: String, values: List) : AnimeFilter.Group(name, values)
+
+ private inline fun AnimeFilterList.getFirst(): R {
+ return this.filterIsInstance().first()
+ }
+
+ private inline fun AnimeFilterList.asQueryPart(): String {
+ return this.filterIsInstance().joinToString("") {
+ (it as QueryPartFilter).toQueryPart()
+ }
+ }
+
+ class AdultFilter : AnimeFilter.CheckBox("Exibir animes adultos", true)
+
+ class FormatFilter : QueryPartFilter("Formato", SKFiltersData.formats)
+ class StatusFilter : QueryPartFilter("Status do anime", SKFiltersData.status)
+ class TypeFilter : QueryPartFilter("Tipo de vídeo", SKFiltersData.types)
+
+ class GenresFilter : CheckBoxFilterList(
+ "Gêneros",
+ SKFiltersData.genres.map { CheckBoxVal(it.first, false) }
+ )
+
+ // Mimicking the order of filters on the source
+ val filterList = AnimeFilterList(
+ TypeFilter(),
+ StatusFilter(),
+ AdultFilter(),
+ FormatFilter(),
+ GenresFilter()
+ )
+
+ data class FilterSearchParams(
+ val adult: Boolean = true,
+ val format: String = "",
+ val genres: List = emptyList(),
+ val status: String = "",
+ val type: String = "",
+ )
+
+ internal fun getSearchParameters(filters: AnimeFilterList): FilterSearchParams {
+ val genres = filters.getFirst().state
+ .mapNotNull { genre ->
+ if (genre.state) {
+ SKFiltersData.genres.find { it.first == genre.name }!!.second
+ } else { null }
+ }.toList()
+
+ return FilterSearchParams(
+ filters.getFirst().state,
+ filters.asQueryPart(),
+ genres,
+ filters.asQueryPart(),
+ filters.asQueryPart()
+ )
+ }
+
+ private object SKFiltersData {
+ val every = Pair("Qualquer um", "")
+
+ val types = arrayOf(
+ every,
+ Pair("Legendado", "1"),
+ Pair("Dublado", "2")
+ )
+
+ val status = arrayOf(
+ every,
+ Pair("Completo", "Completo"),
+ Pair("Em lançamento", "Lançamento")
+ )
+
+ val formats = arrayOf(
+ every,
+ Pair("Anime", "Anime"),
+ Pair("Filme", "Filme")
+ )
+
+ val genres = arrayOf(
+ Pair("Artes Marciais", "12"),
+ Pair("Aventura", "13"),
+ Pair("Ação", "5"),
+ Pair("Boys Love", "1125"),
+ Pair("Carros", "945"),
+ Pair("Chinês", "1032"),
+ Pair("Comédia Romântica", "15"),
+ Pair("Comédia", "14"),
+ Pair("Corrida", "1690"),
+ Pair("Culinária", "576"),
+ Pair("Dementia", "164"),
+ Pair("Demônios", "35"),
+ Pair("Drama", "9"),
+ Pair("Ecchi", "16"),
+ Pair("Erótico", "1203"),
+ Pair("Escolar", "812"),
+ Pair("Espaço", "429"),
+ Pair("Esporte", "17"),
+ Pair("Fantasia", "10"),
+ Pair("Ficção Científica", "18"),
+ Pair("Game", "156"),
+ Pair("Girls Love", "1228"),
+ Pair("Gore", "1708"),
+ Pair("Harém", "69"),
+ Pair("Histórico", "88"),
+ Pair("Horror", "165"),
+ Pair("Idols", "1702"),
+ Pair("Insanidade", "891"),
+ Pair("Isekai", "1138"),
+ Pair("Jogos", "19"),
+ Pair("Josei", "1345"),
+ Pair("Kids", "847"),
+ Pair("Magia", "20"),
+ Pair("Maid", "1677"),
+ Pair("Mecha", "21"),
+ Pair("Militar", "6"),
+ Pair("Mistério", "7"),
+ Pair("Munyuu", "1074"),
+ Pair("Musical", "22"),
+ Pair("Novel", "252"),
+ Pair("Parody", "1197"),
+ Pair("Paródia", "207"),
+ Pair("Performing Arts", "1564"),
+ Pair("Piratas", "172"),
+ Pair("Polícia", "229"),
+ Pair("Psicológico", "50"),
+ Pair("RPG", "94"),
+ Pair("Romance", "23"),
+ Pair("Samurai", "439"),
+ Pair("School", "1065"),
+ Pair("Sci-Fi", "42"),
+ Pair("Seinen", "24"),
+ Pair("Shoujo", "210"),
+ Pair("Shoujo-ai", "25"),
+ Pair("Shounen", "11"),
+ Pair("Shounen-AI", "322"),
+ Pair("Slice of Life", "26"),
+ Pair("Sobrenatural", "27"),
+ Pair("Super Poder", "8"),
+ Pair("Suspense", "230"),
+ Pair("Terror", "28"),
+ Pair("Thriller", "51"),
+ Pair("Tragedia", "269"),
+ Pair("Vampiro", "134"),
+ Pair("Vida Diaria", "253"),
+ Pair("Vida Escolar", "29"),
+ Pair("Violência", "440"),
+ Pair("Yaoi", "612"),
+ Pair("Yuri", "1497")
+ )
+ }
+}
diff --git a/src/pt/sukianimes/src/eu/kanade/tachiyomi/animeextension/pt/sukianimes/SKUrlActivity.kt b/src/pt/sukianimes/src/eu/kanade/tachiyomi/animeextension/pt/sukianimes/SKUrlActivity.kt
new file mode 100644
index 000000000..5829ab1b9
--- /dev/null
+++ b/src/pt/sukianimes/src/eu/kanade/tachiyomi/animeextension/pt/sukianimes/SKUrlActivity.kt
@@ -0,0 +1,41 @@
+package eu.kanade.tachiyomi.animeextension.pt.sukianimes
+
+import android.app.Activity
+import android.content.ActivityNotFoundException
+import android.content.Intent
+import android.os.Bundle
+import android.util.Log
+import kotlin.system.exitProcess
+
+/**
+ * Springboard that accepts https://sukianimes.com/anime/- intents
+ * and redirects them to the main Aniyomi process.
+ */
+class SKUrlActivity : Activity() {
+
+ private val TAG = "SKUrlActivity"
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ val pathSegments = intent?.data?.pathSegments
+ if (pathSegments != null && pathSegments.size > 1) {
+ val item = pathSegments[1]
+ val mainIntent = Intent().apply {
+ action = "eu.kanade.tachiyomi.ANIMESEARCH"
+ putExtra("query", "${SukiAnimes.PREFIX_SEARCH}$item")
+ putExtra("filter", packageName)
+ }
+
+ try {
+ startActivity(mainIntent)
+ } catch (e: ActivityNotFoundException) {
+ Log.e(TAG, e.toString())
+ }
+ } else {
+ Log.e(TAG, "could not parse uri from intent $intent")
+ }
+
+ finish()
+ exitProcess(0)
+ }
+}
diff --git a/src/pt/sukianimes/src/eu/kanade/tachiyomi/animeextension/pt/sukianimes/SukiAnimes.kt b/src/pt/sukianimes/src/eu/kanade/tachiyomi/animeextension/pt/sukianimes/SukiAnimes.kt
new file mode 100644
index 000000000..1a02a793c
--- /dev/null
+++ b/src/pt/sukianimes/src/eu/kanade/tachiyomi/animeextension/pt/sukianimes/SukiAnimes.kt
@@ -0,0 +1,274 @@
+package eu.kanade.tachiyomi.animeextension.pt.sukianimes
+
+import eu.kanade.tachiyomi.animeextension.pt.sukianimes.dto.AnimeDto
+import eu.kanade.tachiyomi.animeextension.pt.sukianimes.dto.SearchResultDto
+import eu.kanade.tachiyomi.animeextension.pt.sukianimes.extractors.BloggerExtractor
+import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
+import eu.kanade.tachiyomi.animesource.model.AnimesPage
+import eu.kanade.tachiyomi.animesource.model.SAnime
+import eu.kanade.tachiyomi.animesource.model.SEpisode
+import eu.kanade.tachiyomi.animesource.model.Video
+import eu.kanade.tachiyomi.animesource.online.ParsedAnimeHttpSource
+import eu.kanade.tachiyomi.network.GET
+import eu.kanade.tachiyomi.network.POST
+import eu.kanade.tachiyomi.network.asObservableSuccess
+import eu.kanade.tachiyomi.util.asJsoup
+import kotlinx.serialization.decodeFromString
+import kotlinx.serialization.json.Json
+import okhttp3.FormBody
+import okhttp3.OkHttpClient
+import okhttp3.Request
+import okhttp3.Response
+import org.jsoup.nodes.Document
+import org.jsoup.nodes.Element
+import rx.Observable
+import uy.kohesive.injekt.api.get
+import kotlin.Exception
+
+class SukiAnimes : ParsedAnimeHttpSource() {
+
+ override val name = "SukiAnimes"
+
+ override val baseUrl = "https://sukianimes.com"
+ private val API_URL = "$baseUrl/wp-admin/admin-ajax.php"
+ private val NONCE_URL = "$baseUrl/?js_global=1&ver=6.1.1"
+
+ override val lang = "pt-BR"
+
+ override val supportsLatest = true
+
+ override val client: OkHttpClient = network.client
+
+ private val json = Json {
+ ignoreUnknownKeys = true
+ }
+
+ // ============================== Popular ===============================
+ // This source doesn't have a popular anime page, so we'll grab
+ // the latest anime additions instead.
+ override fun popularAnimeSelector() = "section.animeslancamentos div.aniItem > a"
+ override fun popularAnimeRequest(page: Int) = GET(baseUrl)
+ override fun popularAnimeNextPageSelector() = null // disable it
+ override fun popularAnimeFromElement(element: Element): SAnime {
+ return SAnime.create().apply {
+ setUrlWithoutDomain(element.attr("href"))
+ title = element.attr("title")
+ thumbnail_url = element.selectFirst("img").attr("src")
+ }
+ }
+
+ // ============================== Episodes ==============================
+ override fun episodeListSelector() = "div.ultEpsContainerItem > a"
+ private fun episodeListNextPageSelector() = latestUpdatesNextPageSelector()
+
+ override fun fetchEpisodeList(anime: SAnime): Observable
> {
+ return client.newCall(episodeListRequest(anime))
+ .asObservableSuccess()
+ .map { response ->
+ val realDoc = getRealDoc(response.asJsoup())
+ episodeListParse(realDoc).reversed()
+ }
+ }
+
+ override fun episodeListParse(response: Response): List {
+ return episodeListParse(response.asJsoup())
+ }
+
+ private fun episodeListParse(doc: Document): List {
+ val episodeList = mutableListOf()
+ val eps = doc.select(episodeListSelector()).map(::episodeFromElement)
+ episodeList.addAll(eps)
+ val nextPageElement = doc.selectFirst(episodeListNextPageSelector())
+ if (nextPageElement != null) {
+ val nextUrl = nextPageElement.attr("href")
+ val res = client.newCall(GET(nextUrl)).execute()
+ episodeList.addAll(episodeListParse(res))
+ }
+ return episodeList
+ }
+
+ override fun episodeFromElement(element: Element): SEpisode {
+ return SEpisode.create().apply {
+ setUrlWithoutDomain(element.attr("href"))
+ val title = element.attr("title")
+ name = title
+ episode_number = runCatching {
+ title.trim().substringAfterLast(" ").toFloat()
+ }.getOrDefault(0F)
+ }
+ }
+
+ // ============================ Video Links =============================
+ override fun videoListParse(response: Response): List