From 9e36c940909a20aa49356d345c5f04d0380514fe Mon Sep 17 00:00:00 2001 From: wasu <61418403+wasu-code@users.noreply.github.com> Date: Fri, 11 Apr 2025 04:31:27 +0200 Subject: [PATCH 1/3] Add Newgrounds.com (#877) * new source: NewGrounds * add icons * add search and search filters * display toast when Adult Content was filtered (login required) * better sorting filter; let user adjust description * if playlist available treat as series * make sure anime is part of series (not playlist or collection) * remove hi_res image * remove unnecessary comment * add NSFW flag * simplify episode url extraction * adjust stats summary in description * use baseUrl in Referer header * eliminate the need for restart after changing preferences --- src/all/newgrounds/build.gradle | 8 + .../res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 4221 bytes .../res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 2312 bytes .../res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 5861 bytes .../res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 11224 bytes .../res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 15946 bytes .../all/newgrounds/DateUtils.kt | 13 + .../all/newgrounds/NewGrounds.kt | 545 ++++++++++++++++++ .../all/newgrounds/NewGroundsFilters.kt | 75 +++ 9 files changed, 641 insertions(+) create mode 100644 src/all/newgrounds/build.gradle create mode 100644 src/all/newgrounds/res/mipmap-hdpi/ic_launcher.png create mode 100644 src/all/newgrounds/res/mipmap-mdpi/ic_launcher.png create mode 100644 src/all/newgrounds/res/mipmap-xhdpi/ic_launcher.png create mode 100644 src/all/newgrounds/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 src/all/newgrounds/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 src/all/newgrounds/src/eu/kanade/tachiyomi/animeextension/all/newgrounds/DateUtils.kt create mode 100644 src/all/newgrounds/src/eu/kanade/tachiyomi/animeextension/all/newgrounds/NewGrounds.kt create mode 100644 src/all/newgrounds/src/eu/kanade/tachiyomi/animeextension/all/newgrounds/NewGroundsFilters.kt diff --git a/src/all/newgrounds/build.gradle b/src/all/newgrounds/build.gradle new file mode 100644 index 00000000..b835c961 --- /dev/null +++ b/src/all/newgrounds/build.gradle @@ -0,0 +1,8 @@ +ext { + extName = 'Newgrounds' + extClass = '.NewGrounds' + extVersionCode = 1 + isNsfw = true +} + +apply from: "$rootDir/common.gradle" diff --git a/src/all/newgrounds/res/mipmap-hdpi/ic_launcher.png b/src/all/newgrounds/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..430ff8b3394f596cd9e8109b3f35d03e7e0f21de GIT binary patch literal 4221 zcmV-@5Q6WCP)Px_H%UZ6RCr$PTnTg(RTjNn-Ps{KfslZK1js0xBLswjVG|r9iV6{eA|j}802PN- zjwm81LI#+_A}GT$VMhT)bP%HuC4giM0Sp=uOh_a^5)zVhda0W8es?F;-L-}8%s8I> zbNWDc|El``{(J9x|5bH#XljZG=$i0RMxY5G6^1kc)C7=cCep+pFENOK{5^pw0HBv@ zQ=2pc=EUZtur9FygLHy1k<;!CV}(I z%l*Oki(XStmZ=R)4Iq8ShF;PA;`wwvU(pTVn*-qLVX-el-CBqjMdJVLBb7d^i-taQ z9xc~(@54ifN-Gzdb1W}x?7-E-&aRipm40v>98t3xHXQMREXa|g8;b$dO0h@@prETe+boC*H@xhGtkcS9@RZvIM+t22$*|<6u)JmJ&DQ}` zH58y`#XH(8Xcb|YQ3sG_a9mlY0Z6SLDsa$g`iT9F&^H4TU;CLZ&3V`Pf3fI?5jA^YQ;CNA5j%zzz zUXG0#JjxEXmjTiMj>E9DMmVkB(~t#FSn0q{d{KdS?G?DW4Yo zS^-j*4fQZv1Q@@=uws8L zic1AdN%p}X<2mT+vM~jws$}jJ)dH69)8R~U1oVOtqms(;XdgX%^iG<$zp0PNUSg0G zFlw@LADG!HVA)<1PTjZ{-S6%J->Pgp-#Z(jffj|;R@%G*JTKt;D;zdt#^a9eqmYx6 zgZAy)!=N)E_SSl&_OrsEmtd0JBW(ax-fkX55_64WQou;D#{IL>N*)te)u1M@H>OUV zii>A6kkWoPS_acLNKOv&09mZSkz5XW=41>XI}cAiH6GE?(RlUMS8@6BWt{kADW><& zMWoYCQk>5T@G?LyOp?uYq-A#edn?}9QH$ew0!BPM0*T>2;rV{~2ni&OH7sOXt-#8U zEHETY!?5A02o4U$v}w~YWy%yBK71JQ@$o1x_!?t6>_<$5gU#*<42!o@x+8_c<;UU zP*YQbX3d)6`t|F$>#n;{SXh8&RmU))XEwr`IZV#IhU5i+IEhSJo1|>(W&?2LeiL5X zX2HC9^YHZ3PvhLVb0{e(!K%N#g=M1)a8H6B24<+@#c*fU^$G#IjtAmrb00*+c0*=n zCcgUWE9i7OJpTCO2nYy3X=y2129+bZeHI3E=E2p~BD@Yzv@OglhH$AU;rtl|pz&$d z_$1qa`Sa(aW5-^H{W_7JD+Ci*_8xVa=K~7(93|Zrr$m4I4I~Q>RV{ z3k!q6U|@1Fo6R_W{5aaRi$lA1?eXDGtw1Y_Bu zLx)gQRK(uz-Mbe%cI?2FD_0N~C17rfABNtoXEL-Uno8Rt*C12>-irV=KxP6^StDTg zF*D|DGNQWH{aEr8YKe)7_~x5$P+MDzfddDkbLY<3v}qG=-MYohoP1`BshCMw{_dMM>Vf(MBr^B~Tp+#y3_(I9w1KzfE5vo_YEvc~=1l>;0) z6NTpK)2A~@k;Lx1?>@|&ITK%e@dXwwTEq;vRjXFG^UgcbrArrNefa^>pSu9xx-EnT z_B0+qtroP3P{^d61mjpy0cH!B=g^+Z>Vl6L96G#Bn>Ls@aU%Tu{BZN;O$HSGON=4& zCZ8gYA|T1sGc%82YSLjObu_@=M=FBKC7iB6+WL9OATdA^5|ddOqe;LkTa4K7sTsT@ zvlj%Gt5C)p8yky3g9f2nw{Fnu^~|6tC%Ss|DwEpq;lmk|2u#Xp2r!Cjg@uI-wEVmr z3~8T>Nket;_XYGiFdx$CB%~&p?TbCgBZ(0}E)vs_6IB@nytv+o?O&PMB&ktkB7X@F z4`(c)iQa$z{g^Rh1`GYvNKTX+?cBK&zx?tG1CqWA3=Bk2P!K!z*s){GvwUhVqJQ^L z_z2}N2sQ9W84{wcxI3OhfUjulA`!X7hVBIh$pIuE$;hCmtu7%%`e)wfVy zT7oYR?}NT59aEEe1p3*}*ysR-I{;FOV$S-e+RWtDHQ1kJWxH!1A0NgDN5)INi4`W3 z34Qza#RCsK!1@tj#5D3F0%P^+)eJyloVd|ul#HFEM~tG4DK$FZ+SMx%b!{0&_oc;A zGjMNsP`FN86cf$h$_*almet_I4?Oc2O7>hEWK7$BLJuVtB~uTQS{d6F zsbV-~Z4C}*TVS%-_E2uYP7p9;$Po1G*%SNs??-xiI;>VJGwJg3a$LK1jR7}ugsA1#e98zS-;50NE|(S6feE>5|b=}L!kQm`?G!oQtCbVm@}&WNgcVzfxHNivcw37 zi@#bid9@K=pSQ{a;{b~$K4HQHeER99%vT(Z{DtLPqEj&)Q05f>%Cvo1Y;F94)1QOqymF>9j{hrhGJYONm(`A3Tu zE!YIaQfu3`EtAHnQ>WP8%F(|4_FJ}rC05b4Lfl;0NKuW-Cd4TE?&Qgn*tLB##?{0xNkCOUQ?4!|G^c zv;+*vD>pZ{?sHdg=smHC5=sJ!d}+ag1^D*FQM^6k-{>0W07#knUIa+`LR5-Ld8-oJ07a@svFv;+MlG&EQ7O-M+f>6@yLK%rPki>-XRM4t zpRZrPp2>>OmnPAq;GivZI|lcNYIzHyl%k zmWpl|%7fz_fWqXX*o_JSFKrIM{>*Zg#gZ40pU?!!py@N3B-MNfJPOAL4jf>g(cX#} zKt@eFb^?H+8Qu6J+0cD0N0Z8>l9Q8J-H5#H?Afyn@VwkC^zeNj$vxB_-mM4CGl``W zgX91b*zMy*hZ6C@=lM(;)aJ~Y!{ju6{CJi{Q;mn}%`^$B7Sel)QayU~z(*f_#3GQR z5rAaslp)K3LtqiO#4x%BNsU51-QC!|VHS4Igs3L1%pT7f5eI}geEd{=ujpD%6TXuOi4)rDH}V(W_T4 zCPQLtQNcxY^gDu?57mlp^Jv1y!!Vy7t`Nm+3E$^AJhtZa!QlHxvbzs^_UvH>P2~#O zX;ZkKJ$p9GcW7ew-g_@&8Og-a$ae@Zs`F4T*mi{8!7+_Cj+WPn^I>^6T7!IG>e*Dp##!I0|jMSvXDcP+#602tMp zw_0UW%lPX1cnDDFf>vR2x1PlV(K2Wa@=^px%QYGXju#}R29TCPYh=^^030s^qy-Eu z{van+4Ge2IK~#_Mg2c2$F}3LB^1Ws=Z^)vkVj=0f~N!5lRc-w+D#pQ62$w zd2`6zHj(;SIvxE^RVx@SGLt+^qc3>}1KG0(0{DV!)}?nmUA5$PehwA*}u{Sfb`l4Fz9qEZV-eSW)@)dEw;&b1Jt3sBY-*#rz)W?HiZ&e88r-C@x9* zk_r=3&1cj=(>6&@dR;0JPR@9@cKnA~rb`X|NhWPA3a96Q)c%M-BTQW5DXjubg(bpI8$YW#`t=1A+QBh4^k?CY z%Td9Go|6c)zX%+sXS+0x>$lX6>M16>=aBEvubpq}4+}UZMUtY&E9gbw{6GI450VhQ z2oSoFM=wRXYb~NUK+)R&x5OCNu{eCj0hZ!ee-9IG)Wv85NCn;|fSLgEOo9Iaq65*; T*(b*A00000NkvXXu0mjfaj@ss literal 0 HcmV?d00001 diff --git a/src/all/newgrounds/res/mipmap-mdpi/ic_launcher.png b/src/all/newgrounds/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..ada390ae2b7a8806813aa1b9d3e7da2789b9dfff GIT binary patch literal 2312 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 literal 0 HcmV?d00001 diff --git a/src/all/newgrounds/res/mipmap-xxhdpi/ic_launcher.png b/src/all/newgrounds/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..ddd9259b6bf967a05e524d44b22e0eea6fd148d0 GIT binary patch literal 11224 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

VVhxJ42gsJr4Jpx480yArs1mzW!;H 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 literal 0 HcmV?d00001 diff --git a/src/all/newgrounds/src/eu/kanade/tachiyomi/animeextension/all/newgrounds/DateUtils.kt b/src/all/newgrounds/src/eu/kanade/tachiyomi/animeextension/all/newgrounds/DateUtils.kt new file mode 100644 index 00000000..1da32290 --- /dev/null +++ b/src/all/newgrounds/src/eu/kanade/tachiyomi/animeextension/all/newgrounds/DateUtils.kt @@ -0,0 +1,13 @@ +import java.text.ParseException +import java.text.SimpleDateFormat + +@Suppress("NOTHING_TO_INLINE") +inline fun SimpleDateFormat.tryParse(date: String?): Long { + date ?: return 0L + + return try { + parse(date)?.time ?: 0L + } catch (_: ParseException) { + 0L + } +} diff --git a/src/all/newgrounds/src/eu/kanade/tachiyomi/animeextension/all/newgrounds/NewGrounds.kt b/src/all/newgrounds/src/eu/kanade/tachiyomi/animeextension/all/newgrounds/NewGrounds.kt new file mode 100644 index 00000000..500c0406 --- /dev/null +++ b/src/all/newgrounds/src/eu/kanade/tachiyomi/animeextension/all/newgrounds/NewGrounds.kt @@ -0,0 +1,545 @@ +package eu.kanade.tachiyomi.animeextension.all.newgrounds + +import android.app.Application +import android.os.Handler +import android.os.Looper +import android.widget.Toast +import androidx.preference.CheckBoxPreference +import androidx.preference.ListPreference +import androidx.preference.MultiSelectListPreference +import androidx.preference.PreferenceScreen +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 +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.util.asJsoup +import okhttp3.FormBody +import okhttp3.Headers +import okhttp3.HttpUrl.Companion.toHttpUrl +import okhttp3.Request +import okhttp3.Response +import org.json.JSONObject +import org.jsoup.nodes.Document +import org.jsoup.nodes.Element +import tryParse +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get +import java.text.SimpleDateFormat +import java.util.Locale + +private const val PAGE_SIZE = 20 + +class NewGrounds : ParsedAnimeHttpSource(), ConfigurableAnimeSource { + + override val lang = "all" + override val baseUrl = "https://www.newgrounds.com" + override val name = "Newgrounds" + override val supportsLatest = true + + private val dateFormat = SimpleDateFormat("MMM dd, yyyy", Locale.ENGLISH) + + private val preferences by lazy { + Injekt.get().getSharedPreferences("source_$id", 0x0000) + } + + private val context = Injekt.get() + private val handler by lazy { Handler(Looper.getMainLooper()) } + + private val videoListHeaders by lazy { + headers.newBuilder() + .add("Accept", "application/json, text/javascript, */*; q=0.01") + .add("X-Requested-With", "XMLHttpRequest") + .add("Referer", baseUrl) + .build() + } + + // Latest + + private fun getLatestSection(): String { + return preferences.getString("LATEST", PREF_SECTIONS["Latest"])!! + } + + override fun latestUpdatesRequest(page: Int): Request { + val offset = (page - 1) * PAGE_SIZE + return GET("$baseUrl/${getLatestSection()}?offset=$offset", headers) + } + + override fun latestUpdatesNextPageSelector(): String = "#load-more-items a" + + override fun latestUpdatesParse(response: Response): AnimesPage { + checkAdultContentFiltered(response.headers) + return super.latestUpdatesParse(response) + } + + override fun latestUpdatesSelector(): String = animeSelector(getLatestSection()) + + override fun latestUpdatesFromElement(element: Element): SAnime { + return animeFromElement(element, getLatestSection()) + } + + // Browse + + private fun getPopularSection(): String { + return preferences.getString("POPULAR", PREF_SECTIONS["Popular"])!! + } + + override fun popularAnimeRequest(page: Int): Request { + val offset = (page - 1) * PAGE_SIZE + return GET("$baseUrl/${getPopularSection()}?offset=$offset", headers) + } + + override fun popularAnimeNextPageSelector(): String = "#load-more-items a" + + override fun popularAnimeParse(response: Response): AnimesPage { + checkAdultContentFiltered(response.headers) + return super.popularAnimeParse(response) + } + + override fun popularAnimeSelector(): String = animeSelector(getPopularSection()) + + override fun popularAnimeFromElement(element: Element): SAnime { + return animeFromElement(element, getPopularSection()) + } + + // Search + + override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request { + val searchUrl = "$baseUrl/search/conduct/movies".toHttpUrl().newBuilder() + .addQueryParameter("page", page.toString()) + + if (query.isNotEmpty()) searchUrl.addQueryParameter("terms", query) + + filters.findInstance().ifFilterSet { + searchUrl.addQueryParameter("match", MATCH_AGAINST.values.elementAt(it.state)) + } + filters.findInstance()?.state + ?.findInstance().ifFilterSet { + searchUrl.addQueryParameter("exact", "1") + } + filters.findInstance()?.state + ?.findInstance().ifFilterSet { + searchUrl.addQueryParameter("any", "1") + } + filters.findInstance().ifFilterSet { + searchUrl.addQueryParameter("user", it.state) + } + filters.findInstance().ifFilterSet { + searchUrl.addQueryParameter("genre", GENRE.values.elementAt(it.state)) + } + filters.findInstance()?.state + ?.findInstance().ifFilterSet { + searchUrl.addQueryParameter("min_length", it.state) + } + filters.findInstance()?.state + ?.findInstance().ifFilterSet { + searchUrl.addQueryParameter("max_length", it.state) + } + filters.findInstance().ifFilterSet { + searchUrl.addQueryParameter("frontpaged", "1") + } + filters.findInstance()?.state + ?.findInstance().ifFilterSet { + searchUrl.addQueryParameter("after", it.state) + } + filters.findInstance()?.state + ?.findInstance().ifFilterSet { + searchUrl.addQueryParameter("before", it.state) + } + filters.findInstance().ifFilterSet { + if (it.state?.index != 0) { + val sortOption = SORTING.values.elementAt(it.state?.index ?: return@ifFilterSet) + val direction = if (it.state?.ascending == true) "asc" else "desc" + searchUrl.addQueryParameter( + "sort", + "$sortOption-$direction", + ) + } + } + filters.findInstance().ifFilterSet { + searchUrl.addQueryParameter("tags", it.state) + } + + return GET(searchUrl.build(), headers) + } + + override fun searchAnimeNextPageSelector(): String = "#results-load-more" + + override fun searchAnimeParse(response: Response): AnimesPage { + checkAdultContentFiltered(response.headers) + return super.searchAnimeParse(response) + } + + override fun searchAnimeSelector(): String = "ul.itemlist li:not(#results-load-more) a" + + override fun searchAnimeFromElement(element: Element): SAnime = animeFromListElement(element) + + // Etc. + + override fun animeDetailsParse(document: Document): SAnime { + fun getStarRating(): String { + val score: Double = document.selectFirst("#score_number")?.text()?.toDouble() ?: 0.0 + val fullStars = score.toInt() + val hasHalfStar = (score % 1) >= 0.5 + val totalStars = if (hasHalfStar) fullStars + 1 else fullStars + val emptyStars = 5 - totalStars + + return "✪".repeat(fullStars) + (if (hasHalfStar) "✪" else "") + "⬤".repeat(emptyStars) + " ($score)" + } + + fun getAdultRating(): String { + val rating = document.selectFirst("#embed_header h2")!!.className().substringAfter("rated-") + return when (rating) { + "e" -> "🟩 Everyone" + "t" -> "🟦 Ages 13+" + "m" -> "🟪 Ages 17+" + "a" -> "🟥 Adults Only" + else -> "❓" + } + } + + fun getStats(): String { + val statsElement = document.selectFirst("#sidestats > dl:first-of-type") + val views = statsElement?.selectFirst("dd:first-of-type")?.text() ?: "?" + val faves = statsElement?.selectFirst("dd:nth-of-type(2)")?.text() ?: "?" + val votes = statsElement?.selectFirst("dd:nth-of-type(3)")?.text() ?: "?" + + return "👀 $views | ❤️ $faves | 🗳️ $votes" + } + + fun prepareDescription(): String { + val descriptionElements = preferences.getStringSet("DESCRIPTION_ELEMENTS", setOf("short")) + ?: return "" + + val shortDescription = document.selectFirst("meta[itemprop=\"description\"]")?.attr("content") + val longDescription = document.selectFirst("#author_comments")?.wholeText() + val statsSummary = "${getAdultRating()} | ${getStarRating()} | ${getStats()}" + + val description = StringBuilder() + + if (descriptionElements.contains("short")) { + description.append(shortDescription) + } + + if (descriptionElements.contains("long")) { + description.append("\n\n" + longDescription) + } + + if (descriptionElements.contains("stats") || preferences.getBoolean("STATS_SUMMARY", false)) { + description.append("\n\n" + statsSummary) + } + + return description.toString() + } + + val relatedPlaylistElement = document.selectFirst("div[id^=\"related_playlists\"] ") + val relatedPlaylistUrl = relatedPlaylistElement?.selectFirst("a:not([id^=\"related_playlists\"])")?.absUrl("href") + val relatedPlaylistName = relatedPlaylistElement?.selectFirst(".detail-title h4")?.text() + val isPartOfSeries = relatedPlaylistUrl?.startsWith("$baseUrl/series") ?: false + + return SAnime.create().apply { + title = relatedPlaylistName.takeIf { isPartOfSeries } + ?: document.selectFirst("h2[itemprop=\"name\"]")!!.text() + description = prepareDescription() + author = document.selectFirst(".authorlinks > div:first-of-type .item-details-main")?.text() + artist = document.select(".authorlinks > div:not(:first-of-type) .item-details-main").joinToString { + it.text() + } + thumbnail_url = document.selectFirst("meta[itemprop=\"thumbnailUrl\"]")?.absUrl("content") + genre = document.select(".tags li a").joinToString { it.text() } + document.selectFirst("div[id^=\"genre-view\"] dt")?.text() + status = SAnime.ONGOING.takeIf { isPartOfSeries } ?: SAnime.COMPLETED + } + } + + override fun episodeListSelector(): String = throw UnsupportedOperationException("Not Used") + + override fun episodeFromElement(element: Element): SEpisode = throw UnsupportedOperationException("Not Used") + + override suspend fun getEpisodeList(anime: SAnime): List { + val response = client.newCall(GET("${baseUrl}${anime.url}", headers)).execute() + val document = response.asJsoup() + + val relatedPlaylistUrl = document.selectFirst("div[id^=\"related_playlists\"] a:not([id^=\"related_playlists\"])")?.absUrl("href") + val isPartOfSeries = relatedPlaylistUrl?.startsWith("$baseUrl/series") ?: false + + val episodes = if (isPartOfSeries) { + val response2 = client.newCall(GET(relatedPlaylistUrl!!, headers)).execute() + val document2 = response2.asJsoup() + parseEpisodeList(document2) + } else { + val dateString = document.selectFirst("#sidestats > dl:nth-of-type(2) > dd:first-of-type")?.text() + + return listOf( + SEpisode.create().apply { + episode_number = 1f + date_upload = dateFormat.tryParse(dateString) + name = document.selectFirst("meta[name=\"title\"]")!!.attr("content") + setUrlWithoutDomain("$baseUrl${anime.url.replace("/view/","/video/")}") + }, + ) + } + + return episodes + } + + override fun episodeListRequest(anime: SAnime): Request = throw UnsupportedOperationException() + + override fun episodeListParse(response: Response): List = throw UnsupportedOperationException() + + private fun parseEpisodeList(document: Document): List { + val ids = document.select("li.visual-link-container").map { it.attr("data-visual-link") } + val formBody = FormBody.Builder() + .add("ids", ids.toString()) + .add("component_params[include_author]", "1") + .add("include_all_suitabilities", "0") + .add("isAjaxRequest", "1") + .build() + + val request = Request.Builder() + .url("$baseUrl/visual-links-fetch") + .post(formBody) + .headers(headers) + .build() + + val response = client.newCall(request).execute() + + val jsonObject = JSONObject(response.body.string()) + val episodes = mutableListOf() + + val simples = jsonObject.getJSONObject("simples") + var index = 1 + for (key in simples.keys()) { + val subObject = simples.getJSONObject(key) + + for (episodeKey in subObject.keys()) { + val episodeData = subObject.getJSONObject(episodeKey) + val uploaderData = episodeData.getJSONObject("user") + + val episode = SEpisode.create().apply { + episode_number = index.toFloat() + name = episodeData.getString("title") + scanlator = uploaderData.getString("user_name") + setUrlWithoutDomain("$baseUrl/portal/video/${episodeData.getString("id")}") + } + + episodes.add(episode) + index++ + } + } + + return episodes.reversed() + } + + override fun videoListRequest(episode: SEpisode): Request = GET("$baseUrl${episode.url}", videoListHeaders) + + override fun videoListParse(response: Response): List