fixed TenshiProvider.kt

This commit is contained in:
Blatzar 2021-12-16 20:33:14 +01:00
parent cbeecb7b8e
commit 917ff0ac4a
4 changed files with 109 additions and 33 deletions

View file

@ -4,10 +4,12 @@ import android.annotation.SuppressLint
import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.annotation.JsonProperty
import com.fasterxml.jackson.module.kotlin.readValue import com.fasterxml.jackson.module.kotlin.readValue
import com.lagradost.cloudstream3.* 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.ExtractorLink
import com.lagradost.cloudstream3.utils.getQualityFromName import com.lagradost.cloudstream3.utils.getQualityFromName
import org.jsoup.Jsoup
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import java.net.URI
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
@ -27,10 +29,10 @@ class TenshiProvider : MainAPI() {
override val name = "Tenshi.moe" override val name = "Tenshi.moe"
override val hasQuickSearch = false override val hasQuickSearch = false
override val hasMainPage = true override val hasMainPage = true
override val supportedTypes = setOf(TvType.Anime, TvType.AnimeMovie, TvType.ONA) override val supportedTypes = setOf(TvType.Anime, TvType.AnimeMovie, TvType.ONA)
private val ddosGuardKiller = DdosGuardKiller(true)
/*private fun loadToken(): Boolean { /*private fun loadToken(): Boolean {
return try { return try {
val response = get(mainUrl) val response = get(mainUrl)
@ -45,7 +47,7 @@ class TenshiProvider : MainAPI() {
override fun getMainPage(): HomePageResponse { override fun getMainPage(): HomePageResponse {
val items = ArrayList<HomePageList>() val items = ArrayList<HomePageList>()
val soup = Jsoup.parse(app.get(mainUrl).text) val soup = app.get(mainUrl, interceptor = ddosGuardKiller).document
for (section in soup.select("#content > section")) { for (section in soup.select("#content > section")) {
try { try {
if (section.attr("id") == "toplist-tabs") { if (section.attr("id") == "toplist-tabs") {
@ -136,7 +138,8 @@ class TenshiProvider : MainAPI() {
val format = SimpleDateFormat("dd 'of' MMM',' yyyy") val format = SimpleDateFormat("dd 'of' MMM',' yyyy")
val newFormat = SimpleDateFormat("dd-MM-yyyy") val newFormat = SimpleDateFormat("dd-MM-yyyy")
val data = format.parse( 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 null
return newFormat.format(data) return newFormat.format(data)
} catch (e: Exception) { } catch (e: Exception) {
@ -197,16 +200,23 @@ class TenshiProvider : MainAPI() {
override fun search(query: String): ArrayList<SearchResponse> { override fun search(query: String): ArrayList<SearchResponse> {
val url = "$mainUrl/anime" val url = "$mainUrl/anime"
var response = app.get(url, params = mapOf("q" to query), cookies = mapOf("loop-view" to "thumb")).text var document = app.get(
var document = Jsoup.parse(response) url,
params = mapOf("q" to query),
cookies = mapOf("loop-view" to "thumb"),
interceptor = ddosGuardKiller
).document
val returnValue = parseSearchPage(document) val returnValue = parseSearchPage(document)
while (!document.select("""a.page-link[rel="next"]""").isEmpty()) { while (!document.select("""a.page-link[rel="next"]""").isEmpty()) {
val link = document.select("""a.page-link[rel="next"]""") val link = document.select("""a.page-link[rel="next"]""")
if (link != null && !link.isEmpty()) { if (link != null && !link.isEmpty()) {
response = app.get(link[0].attr("href"), cookies = mapOf("loop-view" to "thumb")).text document = app.get(
document = Jsoup.parse(response) link[0].attr("href"),
cookies = mapOf("loop-view" to "thumb"),
interceptor = ddosGuardKiller
).document
returnValue.addAll(parseSearchPage(document)) returnValue.addAll(parseSearchPage(document))
} else { } else {
break break
@ -217,22 +227,31 @@ class TenshiProvider : MainAPI() {
} }
override fun load(url: String): LoadResponse { override fun load(url: String): LoadResponse {
var response = app.get(url, cookies = mapOf("loop-view" to "thumb")).text var document = app.get(
var document = Jsoup.parse(response) url,
cookies = mapOf("loop-view" to "thumb"),
interceptor = ddosGuardKiller
).document
val englishTitle = document.selectFirst("span.value > span[title=\"English\"]")?.parent()?.text()?.trim() val englishTitle =
val japaneseTitle = document.selectFirst("span.value > span[title=\"Japanese\"]")?.parent()?.text()?.trim() 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 canonicalTitle = document.selectFirst("header.entry-header > h1.mb-3").text().trim()
val episodeNodes = document.select("li[class*=\"episode\"] > a").toMutableList() val episodeNodes = document.select("li[class*=\"episode\"] > a").toMutableList()
val totalEpisodePages = if (document.select(".pagination").size > 0) 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 else 1
if (totalEpisodePages != null && totalEpisodePages > 1) { if (totalEpisodePages != null && totalEpisodePages > 1) {
for (pageNum in 2..totalEpisodePages) { for (pageNum in 2..totalEpisodePages) {
response = app.get("$url?page=$pageNum", cookies = mapOf("loop-view" to "thumb")).text document = app.get(
document = Jsoup.parse(response) "$url?page=$pageNum",
cookies = mapOf("loop-view" to "thumb"),
interceptor = ddosGuardKiller
).document
episodeNodes.addAll(document.select("li[class*=\"episode\"] > a")) 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 type = document.selectFirst("a[href*=\"$mainUrl/type/\"]")?.text()?.trim()
val synopsis = document.selectFirst(".entry-description > .card-body")?.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 = 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 ?: "")) { return newAnimeLoadResponse(canonicalTitle, url, getType(type ?: "")) {
engName = englishTitle engName = englishTitle
@ -287,8 +308,7 @@ class TenshiProvider : MainAPI() {
subtitleCallback: (SubtitleFile) -> Unit, subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit callback: (ExtractorLink) -> Unit
): Boolean { ): Boolean {
val response = app.get(data).text val soup = app.get(data, interceptor = ddosGuardKiller).document
val soup = Jsoup.parse(response)
data class Quality( data class Quality(
@JsonProperty("src") val src: String, @JsonProperty("src") val src: String,
@ -300,10 +320,12 @@ class TenshiProvider : MainAPI() {
val release = source.text().replace("/", "").trim() val release = source.text().replace("/", "").trim()
val sourceHTML = app.get( val sourceHTML = app.get(
"https://tenshi.moe/embed?v=${source.attr("href").split("v=")[1].split("&")[0]}", "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 ).text
val match = Regex("""sources: (\[(?:.|\s)+?type: ['"]video/.*?['"](?:.|\s)+?])""").find(sourceHTML) val match = Regex("""sources: (\[(?:.|\s)+?type: ['"]video/.*?['"](?:.|\s)+?])""").find(
sourceHTML
)
if (match != null) { if (match != null) {
val qualities = mapper.readValue<List<Quality>>( val qualities = mapper.readValue<List<Quality>>(
match.destructured.component1() match.destructured.component1()
@ -319,15 +341,18 @@ class TenshiProvider : MainAPI() {
"${this.name} $release - " + it.size + "p", "${this.name} $release - " + it.size + "p",
fixUrl(it.src), fixUrl(it.src),
this.mainUrl, this.mainUrl,
getQualityFromName("${it.size}") getQualityFromName("${it.size}"),
headers = getHeaders(
mapOf(),
null,
ddosGuardKiller.savedCookiesMap[URI(this.mainUrl).host] ?: mapOf()
).toMap()
) )
}) })
} }
} }
for (source in sources) { sources.forEach(callback)
callback.invoke(source)
}
return true return true
} }
} }

View file

@ -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<String, Map<String, String>>()
// 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()
}
}

View file

@ -128,7 +128,7 @@ private fun getCache(cacheTime: Int, cacheUnit: TimeUnit): CacheControl {
/** /**
* Referer > Set headers > Set cookies > Default headers > Default Cookies * Referer > Set headers > Set cookies > Default headers > Default Cookies
*/ */
private fun getHeaders( fun getHeaders(
headers: Map<String, String>, headers: Map<String, String>,
referer: String?, referer: String?,
cookie: Map<String, String> cookie: Map<String, String>
@ -137,12 +137,10 @@ private fun getHeaders(
val cookieHeaders = (DEFAULT_COOKIES + cookie) val cookieHeaders = (DEFAULT_COOKIES + cookie)
val cookieMap = val cookieMap =
if (cookieHeaders.isNotEmpty()) mapOf( if (cookieHeaders.isNotEmpty()) mapOf(
"Cookie" to cookieHeaders.entries.joinToString( "Cookie" to cookieHeaders.entries.joinToString() {
separator = "; "
) {
"${it.key}=${it.value};" "${it.key}=${it.value};"
}) else mapOf() }) else mapOf()
val tempHeaders = (DEFAULT_HEADERS + cookieMap + headers + refererMap) val tempHeaders = (DEFAULT_HEADERS + headers + cookieMap + refererMap)
return tempHeaders.toHeaders() return tempHeaders.toHeaders()
} }

View file

@ -10,9 +10,9 @@ import com.bumptech.glide.integration.okhttp3.OkHttpUrlLoader
import com.bumptech.glide.load.engine.DiskCacheStrategy import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.load.model.GlideUrl import com.bumptech.glide.load.model.GlideUrl
import com.bumptech.glide.module.AppGlideModule import com.bumptech.glide.module.AppGlideModule
import com.bumptech.glide.request.Request
import com.bumptech.glide.request.RequestOptions import com.bumptech.glide.request.RequestOptions
import com.bumptech.glide.signature.ObjectKey import com.bumptech.glide.signature.ObjectKey
import com.lagradost.cloudstream3.network.DdosGuardKiller
import com.lagradost.cloudstream3.network.Requests import com.lagradost.cloudstream3.network.Requests
import java.io.InputStream import java.io.InputStream
@ -31,7 +31,12 @@ class GlideModule : AppGlideModule() {
// Needed for DOH // Needed for DOH
// https://stackoverflow.com/a/61634041 // https://stackoverflow.com/a/61634041
override fun registerComponents(context: Context, glide: Glide, registry: Registry) { 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( registry.replace(
GlideUrl::class.java, GlideUrl::class.java,
InputStream::class.java, InputStream::class.java,