From 917ff0ac4a1b556ffe3331bd2e16466352fa7224 Mon Sep 17 00:00:00 2001 From: Blatzar <46196380+Blatzar@users.noreply.github.com> Date: Thu, 16 Dec 2021 20:33:14 +0100 Subject: [PATCH] fixed TenshiProvider.kt --- .../animeproviders/TenshiProvider.kt | 77 ++++++++++++------- .../cloudstream3/network/DdosGuardKiller.kt | 48 ++++++++++++ .../cloudstream3/network/Requests.kt | 8 +- .../lagradost/cloudstream3/utils/GlideApp.kt | 9 ++- 4 files changed, 109 insertions(+), 33 deletions(-) create mode 100644 app/src/main/java/com/lagradost/cloudstream3/network/DdosGuardKiller.kt diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/TenshiProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/TenshiProvider.kt index 7831c2ff..9500fa9a 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/TenshiProvider.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/TenshiProvider.kt @@ -4,10 +4,12 @@ import android.annotation.SuppressLint import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.module.kotlin.readValue import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.network.DdosGuardKiller +import com.lagradost.cloudstream3.network.getHeaders import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.getQualityFromName -import org.jsoup.Jsoup import org.jsoup.nodes.Document +import java.net.URI import java.text.SimpleDateFormat import java.util.* @@ -27,10 +29,10 @@ class TenshiProvider : MainAPI() { override val name = "Tenshi.moe" override val hasQuickSearch = false override val hasMainPage = true - - override val supportedTypes = setOf(TvType.Anime, TvType.AnimeMovie, TvType.ONA) + private val ddosGuardKiller = DdosGuardKiller(true) + /*private fun loadToken(): Boolean { return try { val response = get(mainUrl) @@ -45,7 +47,7 @@ class TenshiProvider : MainAPI() { override fun getMainPage(): HomePageResponse { val items = ArrayList() - val soup = Jsoup.parse(app.get(mainUrl).text) + val soup = app.get(mainUrl, interceptor = ddosGuardKiller).document for (section in soup.select("#content > section")) { try { if (section.attr("id") == "toplist-tabs") { @@ -136,7 +138,8 @@ class TenshiProvider : MainAPI() { val format = SimpleDateFormat("dd 'of' MMM',' yyyy") val newFormat = SimpleDateFormat("dd-MM-yyyy") val data = format.parse( - dateString.replace("th ", " ").replace("st ", " ").replace("nd ", " ").replace("rd ", " ") + dateString.replace("th ", " ").replace("st ", " ").replace("nd ", " ") + .replace("rd ", " ") ) ?: return null return newFormat.format(data) } catch (e: Exception) { @@ -197,16 +200,23 @@ class TenshiProvider : MainAPI() { override fun search(query: String): ArrayList { val url = "$mainUrl/anime" - var response = app.get(url, params = mapOf("q" to query), cookies = mapOf("loop-view" to "thumb")).text - var document = Jsoup.parse(response) + var document = app.get( + url, + params = mapOf("q" to query), + cookies = mapOf("loop-view" to "thumb"), + interceptor = ddosGuardKiller + ).document val returnValue = parseSearchPage(document) while (!document.select("""a.page-link[rel="next"]""").isEmpty()) { val link = document.select("""a.page-link[rel="next"]""") if (link != null && !link.isEmpty()) { - response = app.get(link[0].attr("href"), cookies = mapOf("loop-view" to "thumb")).text - document = Jsoup.parse(response) + document = app.get( + link[0].attr("href"), + cookies = mapOf("loop-view" to "thumb"), + interceptor = ddosGuardKiller + ).document returnValue.addAll(parseSearchPage(document)) } else { break @@ -217,22 +227,31 @@ class TenshiProvider : MainAPI() { } override fun load(url: String): LoadResponse { - var response = app.get(url, cookies = mapOf("loop-view" to "thumb")).text - var document = Jsoup.parse(response) + var document = app.get( + url, + cookies = mapOf("loop-view" to "thumb"), + interceptor = ddosGuardKiller + ).document - val englishTitle = document.selectFirst("span.value > span[title=\"English\"]")?.parent()?.text()?.trim() - val japaneseTitle = document.selectFirst("span.value > span[title=\"Japanese\"]")?.parent()?.text()?.trim() + val englishTitle = + document.selectFirst("span.value > span[title=\"English\"]")?.parent()?.text()?.trim() + val japaneseTitle = + document.selectFirst("span.value > span[title=\"Japanese\"]")?.parent()?.text()?.trim() val canonicalTitle = document.selectFirst("header.entry-header > h1.mb-3").text().trim() val episodeNodes = document.select("li[class*=\"episode\"] > a").toMutableList() val totalEpisodePages = if (document.select(".pagination").size > 0) - document.select(".pagination .page-item a.page-link:not([rel])").last().text().toIntOrNull() + document.select(".pagination .page-item a.page-link:not([rel])").last().text() + .toIntOrNull() else 1 if (totalEpisodePages != null && totalEpisodePages > 1) { for (pageNum in 2..totalEpisodePages) { - response = app.get("$url?page=$pageNum", cookies = mapOf("loop-view" to "thumb")).text - document = Jsoup.parse(response) + document = app.get( + "$url?page=$pageNum", + cookies = mapOf("loop-view" to "thumb"), + interceptor = ddosGuardKiller + ).document episodeNodes.addAll(document.select("li[class*=\"episode\"] > a")) } } @@ -260,10 +279,12 @@ class TenshiProvider : MainAPI() { val type = document.selectFirst("a[href*=\"$mainUrl/type/\"]")?.text()?.trim() val synopsis = document.selectFirst(".entry-description > .card-body")?.text()?.trim() - val genre = document.select("li.genre.meta-data > span.value").map { it?.text()?.trim().toString() } + val genre = + document.select("li.genre.meta-data > span.value").map { it?.text()?.trim().toString() } val synonyms = - document.select("li.synonym.meta-data > div.info-box > span.value").map { it?.text()?.trim().toString() } + document.select("li.synonym.meta-data > div.info-box > span.value") + .map { it?.text()?.trim().toString() } return newAnimeLoadResponse(canonicalTitle, url, getType(type ?: "")) { engName = englishTitle @@ -287,8 +308,7 @@ class TenshiProvider : MainAPI() { subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit ): Boolean { - val response = app.get(data).text - val soup = Jsoup.parse(response) + val soup = app.get(data, interceptor = ddosGuardKiller).document data class Quality( @JsonProperty("src") val src: String, @@ -300,10 +320,12 @@ class TenshiProvider : MainAPI() { val release = source.text().replace("/", "").trim() val sourceHTML = app.get( "https://tenshi.moe/embed?v=${source.attr("href").split("v=")[1].split("&")[0]}", - headers = mapOf("Referer" to data) + headers = mapOf("Referer" to data), interceptor = ddosGuardKiller ).text - val match = Regex("""sources: (\[(?:.|\s)+?type: ['"]video/.*?['"](?:.|\s)+?])""").find(sourceHTML) + val match = Regex("""sources: (\[(?:.|\s)+?type: ['"]video/.*?['"](?:.|\s)+?])""").find( + sourceHTML + ) if (match != null) { val qualities = mapper.readValue>( match.destructured.component1() @@ -319,15 +341,18 @@ class TenshiProvider : MainAPI() { "${this.name} $release - " + it.size + "p", fixUrl(it.src), this.mainUrl, - getQualityFromName("${it.size}") + getQualityFromName("${it.size}"), + headers = getHeaders( + mapOf(), + null, + ddosGuardKiller.savedCookiesMap[URI(this.mainUrl).host] ?: mapOf() + ).toMap() ) }) } } - for (source in sources) { - callback.invoke(source) - } + sources.forEach(callback) return true } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/network/DdosGuardKiller.kt b/app/src/main/java/com/lagradost/cloudstream3/network/DdosGuardKiller.kt new file mode 100644 index 00000000..96acce70 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/network/DdosGuardKiller.kt @@ -0,0 +1,48 @@ +package com.lagradost.cloudstream3.network + +import com.lagradost.cloudstream3.app +import okhttp3.Interceptor +import okhttp3.Request +import okhttp3.Response + +/** + * @param alwaysBypass will pre-emptively fetch ddos guard cookies if true. + * If false it will only try to get cookies when a request returns 403 + * */ +class DdosGuardKiller(private val alwaysBypass: Boolean) : Interceptor { + val savedCookiesMap = mutableMapOf>() + + // As seen in https://github.com/anime-dl/anime-downloader/blob/master/anime_downloader/sites/erairaws.py + private val resp = app.get( + "https://check.ddos-guard.net/check.js" + ).text + private val ddosBypassPath = Regex("'(.*?)'").find(resp)?.groupValues?.get(1) + + override fun intercept(chain: Interceptor.Chain): Response { + val request = chain.request() + if (alwaysBypass) return bypassDdosGuard(request) + + val response = chain.proceed(request) + return if (response.code == 403){ + bypassDdosGuard(request) + } else response + } + + private fun bypassDdosGuard(request: Request): Response { + val cookies = + savedCookiesMap[request.url.host] + // If no cookies are found fetch and save em. + ?: (request.url.scheme + "://" + request.url.host + (ddosBypassPath ?: "")).let { + app.get(it, cacheTime = 0).cookies.also { cookies -> + savedCookiesMap[request.url.host] = cookies + } + } + + val headers = getHeaders(request.headers.toMap(), null, cookies) + return app.baseClient.newCall( + request.newBuilder() + .headers(headers) + .build() + ).execute() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/network/Requests.kt b/app/src/main/java/com/lagradost/cloudstream3/network/Requests.kt index c0e20588..923c80a8 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/network/Requests.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/network/Requests.kt @@ -128,7 +128,7 @@ private fun getCache(cacheTime: Int, cacheUnit: TimeUnit): CacheControl { /** * Referer > Set headers > Set cookies > Default headers > Default Cookies */ -private fun getHeaders( +fun getHeaders( headers: Map, referer: String?, cookie: Map @@ -137,12 +137,10 @@ private fun getHeaders( val cookieHeaders = (DEFAULT_COOKIES + cookie) val cookieMap = if (cookieHeaders.isNotEmpty()) mapOf( - "Cookie" to cookieHeaders.entries.joinToString( - separator = "; " - ) { + "Cookie" to cookieHeaders.entries.joinToString() { "${it.key}=${it.value};" }) else mapOf() - val tempHeaders = (DEFAULT_HEADERS + cookieMap + headers + refererMap) + val tempHeaders = (DEFAULT_HEADERS + headers + cookieMap + refererMap) return tempHeaders.toHeaders() } diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/GlideApp.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/GlideApp.kt index 7d2afae1..0fdfb922 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/GlideApp.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/GlideApp.kt @@ -10,9 +10,9 @@ import com.bumptech.glide.integration.okhttp3.OkHttpUrlLoader import com.bumptech.glide.load.engine.DiskCacheStrategy import com.bumptech.glide.load.model.GlideUrl import com.bumptech.glide.module.AppGlideModule -import com.bumptech.glide.request.Request import com.bumptech.glide.request.RequestOptions import com.bumptech.glide.signature.ObjectKey +import com.lagradost.cloudstream3.network.DdosGuardKiller import com.lagradost.cloudstream3.network.Requests import java.io.InputStream @@ -31,7 +31,12 @@ class GlideModule : AppGlideModule() { // Needed for DOH // https://stackoverflow.com/a/61634041 override fun registerComponents(context: Context, glide: Glide, registry: Registry) { - val client = Requests().initClient(context) + val client = + Requests().initClient(context) + .newBuilder() + .addInterceptor(DdosGuardKiller(false)) + .build() + registry.replace( GlideUrl::class.java, InputStream::class.java,