$cuz_Gu!$TZ}!kJ80RZN4Fa
z+5Y(k#Cd&)20(i}d-kj<_*+iiKJikht}aRoY5E%^(U2UVCuEj^!cjV0!f0=7Y%J{?
z6ysJznm$ecp;uuDN=X>fj11fBn#;-(k;Lsl(fW*@bco@p*IZ2_&G}a)Nq*RQ&zPD~
zq)mbT3LD$nn#SPBH#Ih%K1H1XG{dWtxU@gbOoPQnl{VFPA-!YEz)hi_fM5=K5g@J$6G#VbXHn6Jdq;Aq5=0tXG@E*Xn7mzhcsQ6zd?*;_=#6BlcKPAw^irkePM(Ad
z36Wp%y6&k*6{TrS5NVT6Ym_Jh9fa{AS%MBl$Vy&R)^9Y1>)ru;NUNb#H^%Sxbhg$T
z2sjFn5yc5GE`S84P-nQwfj+PnJ`a>yA2sYPs4%8VKSH893?&Ba3Bt~geur2
zRe%`u30+jgtJl4=eWjCy1`yk%kTreQ;IxOVEFYg##Jw3Hrox5Fx#ky^yJbxcoOHaA
zGQ`Ex*26B3=r@9nGDqvF00^U0@h%u^{lMJx}BIf<>gwW
z<)t&<3;vhWuW`6Ce*ukU7bHEf{|Ey9$^{m{`y@8;wsqtvD)p7?!|;hLi+K2JkV+k$Hwg9MD38K*|16j6mm-SCs#_LDoG)hMCvl^7hWLd$MVKByc
z&Hg-69C_F3I}6xw2fS+MOMsZEH0`f-K>UJBegr*}C*Q)XVUuTrOG#K6PM9SN?U4AG
z5b_l!YnJG%JVNwbL|UThZJ)w}_#?^?2UKY<$P+%yPCv`FP5GelbaYD;Vfu2SU6q~C
zy#0TKAY6wEJ24Txv?^?W$|l@qL2gypimK293i8Ym#>2j$|RAnx;ra
z%}kHewS1+3KL=v|!CSc1673-WC?Ta4RDn3Oh1rEB;Jl3Kzz~Z2gG`m8^fEvDz*u
z`Y~^wo=wBEB{~>0KXqcy0DEIqW)m+cRs)C`fG()qg&RLwM%Avd2K%VX+SSJt`k)7W
zP=u5MUFIX%+ASUu25ogN#h5p_6wh*p$gvr+$VqLk!&i!
zk^st*uXHv8TUY<4hlOKYHup>6?yZoc(-GG=u;HqM8_ATh$(ZVL-&2A^H~NqN09tTzHfN`e
z6i?|ewWkWci+4T8w~EvI2;E(|eKWRoIu*7gus72X57ZLc#CCJ|P2&WwIRMQFwh}OR
zfC4flRQ@59JP7YFvFG0ZSF?6Dribx_F5n6`lnJqWr`r22Zm)2h-k~;MLLsq$Fp`}j
zfmd~HuV*K+b1%a)SCBZ=0;=F%-8tx$hd@#fdd*VzH@$QV!T}Afd$UtK3SAoXvxsJW
z%{16baS26lMr|U^2KatT+H9V60}>Y$t-(v^|7Z|X@a;_m=9Dq$fl%Px%9w25YR;bv
zs3G+ri})SXEpH{S9Ky&X*C(1z9sL6wvUbEFTEC~D4`t)@6D1e#;1O!qyyrq!;$yWh|;+zXwV!DX{w0vJ=
z&d&x0u6O&Ylp8io1wKBB?fh*y6ld1&Bi{ka|#NW0d^LcUw{u4dIPZ50Etl`GQdcY#$b5%9qcV-GyNTu9}(#E
z2=A8bupA)|n#Yngt(>3+T>WPe3qAeHc!dGYT94;zCElLwac%JN)$PwDr~h!ZWWlB%
zWdXN2vAN~Jn;`P#D6^FFpp4L7ij~zKI6_{Dw&3s`oam;j`<)httD|^YWm%y|#p^gD
z3f>+bpj@)nqIF;g13FqkDXDDHfm1S`?@GO~`EuZT!f90}X<`bV_$Q%%k`G*!cC4E2
zJD&&A`q4{5AEcGCtnWnyOA!q~5x0JSA6|uo0~~H$V`J=Q5ED)O5_dmAofrD^qw
zBuNUTsd6&0-&YgSnw?`Re#iDFcAxgsLNfT5Eq1L<&+*@
z#daQi7%MThc;GW4n4SwyNl&|MtVyUrLMvwg_gsbLnyh&-*tMNyCIkI|=T#8B$xaeW
z3&_f!`l_1v+S&NpOT)bFfelC)=$i2fO7RJb8VX8Ci--U(UO_==LBXtu7NY-afV-!i
zvqRATf4~+PR0j;;`afszb#}M+@wIV({r`>;1rGnePqLcC=>&`cy-)?@(aP4*{|jzV
BTyOvY
literal 0
HcmV?d00001
diff --git a/src/pt/hinatasoul/src/eu/kanade/tachiyomi/animeextension/pt/hinatasoul/HSUrlActivity.kt b/src/pt/hinatasoul/src/eu/kanade/tachiyomi/animeextension/pt/hinatasoul/HSUrlActivity.kt
new file mode 100644
index 000000000..ddd1f1b8f
--- /dev/null
+++ b/src/pt/hinatasoul/src/eu/kanade/tachiyomi/animeextension/pt/hinatasoul/HSUrlActivity.kt
@@ -0,0 +1,42 @@
+package eu.kanade.tachiyomi.animeextension.pt.hinatasoul
+
+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://www.hinatasoul.com/animes/ intents
+ * and redirects them to the main Aniyomi process.
+ */
+class HSUrlActivity : Activity() {
+
+ private val TAG = "HSUrlActivity"
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ val pathSegments = intent?.data?.pathSegments
+ if (pathSegments != null && pathSegments.size > 1) {
+ val slug = pathSegments[1]
+ val searchQuery = HinataSoul.PREFIX_SEARCH + slug
+ val mainIntent = Intent().apply {
+ action = "eu.kanade.tachiyomi.ANIMESEARCH"
+ putExtra("query", searchQuery)
+ 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/hinatasoul/src/eu/kanade/tachiyomi/animeextension/pt/hinatasoul/HinataSoul.kt b/src/pt/hinatasoul/src/eu/kanade/tachiyomi/animeextension/pt/hinatasoul/HinataSoul.kt
new file mode 100644
index 000000000..6fee34735
--- /dev/null
+++ b/src/pt/hinatasoul/src/eu/kanade/tachiyomi/animeextension/pt/hinatasoul/HinataSoul.kt
@@ -0,0 +1,267 @@
+package eu.kanade.tachiyomi.animeextension.pt.hinatasoul
+
+import android.app.Application
+import android.content.SharedPreferences
+import androidx.preference.ListPreference
+import androidx.preference.PreferenceScreen
+import eu.kanade.tachiyomi.animeextension.pt.hinatasoul.extractors.HinataSoulExtractor
+import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
+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.asObservableSuccess
+import eu.kanade.tachiyomi.util.asJsoup
+import okhttp3.Headers
+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.Injekt
+import uy.kohesive.injekt.api.get
+import java.text.SimpleDateFormat
+import java.util.Locale
+
+class HinataSoul : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
+
+ override val name = "Hinata Soul"
+
+ override val baseUrl = "https://www.hinatasoul.com"
+
+ override val lang = "pt-BR"
+
+ override val supportsLatest = true
+
+ override val client: OkHttpClient = network.cloudflareClient
+
+ private val preferences: SharedPreferences by lazy {
+ Injekt.get().getSharedPreferences("source_$id", 0x0000)
+ }
+
+ override fun headersBuilder(): Headers.Builder = Headers.Builder()
+ .add("Referer", baseUrl)
+
+ // ============================== Popular ===============================
+ override fun popularAnimeSelector() = "div.FsssItem:contains(Mais Vistos) > a"
+ override fun popularAnimeRequest(page: Int): Request = GET(baseUrl)
+ override fun popularAnimeFromElement(element: Element) = SAnime.create().apply {
+ setUrlWithoutDomain(element.attr("href"))
+ title = element.text()
+ }
+ override fun popularAnimeNextPageSelector(): String? = null
+
+ // ============================== Episodes ==============================
+ override fun episodeListSelector() = "div.aniContainer a"
+ override fun episodeListParse(response: Response): List {
+ val totalEpisodes = mutableListOf()
+ var doc = getRealDoc(response.asJsoup())
+ val originalUrl = doc.location()
+ var pageNum = 1
+ do {
+ if (pageNum > 1) {
+ doc = client.newCall(GET(originalUrl + "/page/$pageNum"))
+ .execute()
+ .asJsoup()
+ }
+ doc.select(episodeListSelector()).forEach {
+ totalEpisodes.add(episodeFromElement(it))
+ }
+ pageNum++
+ } while (hasNextPage(doc))
+ return totalEpisodes.reversed()
+ }
+
+ override fun episodeFromElement(element: Element) = SEpisode.create().apply {
+ val title = element.attr("title")
+ setUrlWithoutDomain(element.attr("href"))
+ name = title
+ episode_number = runCatching { title.substringAfterLast(" ").toFloat() }
+ .getOrNull() ?: 0F
+ date_upload = element.selectFirst("div.lancaster_episodio_info_data")
+ .text()
+ .toDate()
+ }
+
+ // ============================ Video Links =============================
+ override fun videoListParse(response: Response): List