diff --git a/src/en/nineanime/build.gradle b/src/en/nineanime/build.gradle
index 88afe35f1..033d62717 100644
--- a/src/en/nineanime/build.gradle
+++ b/src/en/nineanime/build.gradle
@@ -1,16 +1,19 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
+apply plugin: 'kotlinx-serialization'
ext {
extName = '9anime'
pkgNameSuffix = 'en.nineanime'
extClass = '.NineAnime'
- extVersionCode = 31
+ extVersionCode = 32
libVersion = '13'
}
dependencies {
compileOnly libs.bundles.coroutines
+ implementation (project(':lib-streamtape-extractor'))
+ implementation "dev.datlag.jsunpacker:jsunpacker:1.0.1"
}
apply from: "$rootDir/common.gradle"
diff --git a/src/en/nineanime/src/eu/kanade/tachiyomi/animeextension/en/nineanime/JsVrfInterceptor.kt b/src/en/nineanime/src/eu/kanade/tachiyomi/animeextension/en/nineanime/JsVrfInterceptor.kt
index 3052c2bb6..3cfbb51d6 100644
--- a/src/en/nineanime/src/eu/kanade/tachiyomi/animeextension/en/nineanime/JsVrfInterceptor.kt
+++ b/src/en/nineanime/src/eu/kanade/tachiyomi/animeextension/en/nineanime/JsVrfInterceptor.kt
@@ -21,6 +21,7 @@ class JsVrfInterceptor(private val baseUrl: String) {
fun wake() = ""
fun getVrf(query: String): String {
+ if (query.isBlank()) return ""
val jscript = getJs(query)
val cdl = CountDownLatch(1)
var vrf = ""
diff --git a/src/en/nineanime/src/eu/kanade/tachiyomi/animeextension/en/nineanime/NineAnime.kt b/src/en/nineanime/src/eu/kanade/tachiyomi/animeextension/en/nineanime/NineAnime.kt
index 910910b6d..fe9c7658d 100644
--- a/src/en/nineanime/src/eu/kanade/tachiyomi/animeextension/en/nineanime/NineAnime.kt
+++ b/src/en/nineanime/src/eu/kanade/tachiyomi/animeextension/en/nineanime/NineAnime.kt
@@ -11,6 +11,8 @@ 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.lib.streamtapeextractor.StreamTapeExtractor
+import eu.kanade.tachiyomi.animeextension.en.nineanime.extractors.Mp4uploadExtractor
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.util.asJsoup
@@ -18,6 +20,8 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.runBlocking
+import kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
@@ -58,161 +62,40 @@ class NineAnime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
return Headers.Builder().add("Referer", baseUrl)
}
- override fun popularAnimeSelector(): String = "div.ani.items > div"
+ // ============================== Popular ===============================
override fun popularAnimeRequest(page: Int): Request {
- // make the vrf webview available beforehand. please find another solution for this :)
+ // make the vrf webview available beforehand
vrfInterceptor.wake()
return GET("$baseUrl/filter?sort=trending&page=$page")
}
+ override fun popularAnimeSelector(): String = "div.ani.items > div"
+
override fun popularAnimeFromElement(element: Element) = SAnime.create().apply {
setUrlWithoutDomain(element.select("a.name").attr("href").substringBefore("?"))
thumbnail_url = element.select("div.poster img").attr("src")
title = element.select("a.name").text()
}
- override fun popularAnimeNextPageSelector(): String = "nav > ul.pagination > li > a[aria-label=pagination.next]"
+ override fun popularAnimeNextPageSelector(): String =
+ "nav > ul.pagination > li > a[aria-label=pagination.next]"
- override fun episodeListRequest(anime: SAnime): Request {
- val id = client.newCall(GET(baseUrl + anime.url)).execute().asJsoup().selectFirst("div[data-id]").attr("data-id")
- val vrf = vrfInterceptor.getVrf(id)
- return GET("$baseUrl/ajax/episode/list/$id?vrf=${java.net.URLEncoder.encode(vrf, "utf-8")}", headers = Headers.headersOf("url", anime.url))
+ // =============================== Latest ===============================
+
+ override fun latestUpdatesRequest(page: Int): Request {
+ // make the vrf webview available beforehand.
+ vrfInterceptor.wake()
+ return GET("$baseUrl/filter?sort=recently_updated&page=$page")
}
- private fun Iterable.parallelMap(f: suspend (A) -> B): List =
- runBlocking {
- map { async(Dispatchers.Default) { f(it) } }.awaitAll()
- }
+ override fun latestUpdatesSelector(): String = popularAnimeSelector()
- override fun episodeListParse(response: Response): List {
- val animeUrl = response.request.header("url").toString()
- val responseObject = json.decodeFromString(response.body!!.string())
- val document = Jsoup.parse(JSONUtil.unescape(responseObject["result"]!!.jsonPrimitive.content))
- val episodeElements = document.select(episodeListSelector())
- return episodeElements.parallelMap { episodeFromElements(it, animeUrl) }.reversed()
- }
+ override fun latestUpdatesFromElement(element: Element) = popularAnimeFromElement(element)
- override fun episodeListSelector() = "div.episodes ul > li > a"
+ override fun latestUpdatesNextPageSelector(): String = popularAnimeNextPageSelector()
- private fun episodeFromElements(element: Element, url: String): SEpisode {
- val episode = SEpisode.create()
- val epNum = element.attr("data-num")
- val ids = element.attr("data-ids")
- val sub = element.attr("data-sub").toInt().toBoolean()
- val dub = element.attr("data-dub").toInt().toBoolean()
- episode.url = "/ajax/server/list/$ids?vrf=&epurl=$url/ep-$epNum"
- episode.episode_number = epNum.toFloat()
- val langPrefix = "[" + if (sub) {
- "Sub"
- } else {
- ""
- } + if (dub) {
- ",Dub"
- } else {
- ""
- } + "]"
- val name = element.parent()?.select("span.d-title")?.text().orEmpty()
- val namePrefix = "Episode $epNum"
- episode.name = "Episode $epNum" + if (sub || dub) {
- ": $langPrefix"
- } else {
- ""
- } + if (name.isNotEmpty() && name != namePrefix) {
- " $name"
- } else {
- ""
- }
- return episode
- }
-
- override fun episodeFromElement(element: Element): SEpisode = throw Exception("not Used")
-
- private fun Int.toBoolean() = this == 1
-
- override fun videoListRequest(episode: SEpisode): Request {
- val ids = episode.url.substringAfter("list/").substringBefore("?vrf")
- val vrf = vrfInterceptor.getVrf(ids)
- val url = "/ajax/server/list/$ids?vrf=${java.net.URLEncoder.encode(vrf, "utf-8")}"
- val epurl = episode.url.substringAfter("epurl=")
- return GET(baseUrl + url, headers = Headers.headersOf("url", epurl))
- }
-
- override fun videoListParse(response: Response): List