11package com.hexated
22
3+ import app.cash.quickjs.QuickJs
34import com.lagradost.cloudstream3.*
45import com.lagradost.cloudstream3.LoadResponse.Companion.addAniListId
56import com.lagradost.cloudstream3.LoadResponse.Companion.addMalId
@@ -11,6 +12,7 @@ import com.lagradost.cloudstream3.utils.newExtractorLink
1112import kotlinx.coroutines.delay
1213import org.jsoup.Jsoup
1314import org.jsoup.nodes.Element
15+ import java.net.URI
1416import java.util.Calendar
1517
1618class KuramanimeProvider : MainAPI () {
@@ -178,8 +180,8 @@ class KuramanimeProvider : MainAPI() {
178180 }
179181 }
180182
181- private suspend fun invokeLocalSource (url : String , server : String , headers : Map <String , String >, subtitleCallback : (SubtitleFile ) -> Unit , callback : (ExtractorLink ) -> Unit ) {
182- val request = app.post(url, data = mapOf (" authorization" to getAuth()), headers = headers, cookies = cookies)
183+ private suspend fun invokeLocalSource (url : String , server : String , headers : Map <String , String >, authScriptUrl : String , refererUrl : String , subtitleCallback : (SubtitleFile ) -> Unit , callback : (ExtractorLink ) -> Unit ) {
184+ val request = app.post(url, data = mapOf (" authorization" to getAuth(authScriptUrl, refererUrl )), headers = headers, cookies = cookies)
183185 delay(2000 )
184186 val document = request.document
185187 document.select(" video#player > source" ).map {
@@ -203,6 +205,8 @@ class KuramanimeProvider : MainAPI() {
203205
204206 val token = res.selectFirst(" meta[name=csrf-token]" )?.attr(" content" ) ? : return false
205207 val dataKps = res.selectFirst(" [data-kk]" )?.attr(" data-kk" ) ? : return false
208+ val tokenAuthUrl = res.selectFirst(" input#tokenAuthJs" )?.attr(" value" )
209+ val authScriptUrl = if (tokenAuthUrl != null ) " $mainUrl$tokenAuthUrl " else " $mainUrl /storage/leviathan.js?v=${System .currentTimeMillis()} "
206210
207211 val assets = getAssets(dataKps)
208212 var headers = mapOf (
@@ -223,9 +227,9 @@ class KuramanimeProvider : MainAPI() {
223227 val server = source.attr(" value" )
224228 val link = " $data ?${assets.MIX_PAGE_TOKEN_KEY } =$tokenKey &${assets.MIX_STREAM_SERVER_KEY } =$server "
225229 if (server.contains(Regex (" (?i)kuramadrive|archive" ))) {
226- invokeLocalSource(link, server, headers, subtitleCallback, callback)
230+ invokeLocalSource(link, server, headers, authScriptUrl, data, subtitleCallback, callback)
227231 } else {
228- val request = app.post(link, data = mapOf (" authorization" to getAuth()), referer = data, headers = headers, cookies = cookies)
232+ val request = app.post(link, data = mapOf (" authorization" to getAuth(authScriptUrl, data )), referer = data, headers = headers, cookies = cookies)
229233 delay(2000 )
230234 request.document.select(" div.iframe-container iframe" ).attr(" src" ).let { videoUrl ->
231235 loadExtractor(fixUrl(videoUrl), " $mainUrl /" , subtitleCallback, callback)
@@ -247,20 +251,78 @@ class KuramanimeProvider : MainAPI() {
247251 )
248252 }
249253
250- suspend fun getAuth (): String {
251- return authorization ? : fetchAuth().also { authorization = it }
254+ suspend fun getAuth (tokenUrl : String , referer : String ): String {
255+ return authorization ? : fetchAuth(tokenUrl, referer ).also { authorization = it }
252256 }
253257
254- suspend fun fetchAuth (): String {
255- val responseText = app.get(" $mainUrl /misc/token/refresh-token" , cookies = cookies).text
258+ suspend fun fetchAuth (tokenUrl : String , referer : String ): String {
259+ val jsReqHeaders = mapOf (
260+ " Accept" to " */*" ,
261+ " Referer" to referer,
262+ " X-Requested-With" to " XMLHttpRequest"
263+ )
256264
257- val token = Regex (""" "token"\s*:\s*"([^"]+)"""" ).find(responseText)?.groupValues?.get(1 )
265+ val jsCode = app.get(tokenUrl, headers = jsReqHeaders, cookies = cookies).text
266+
267+ if (jsCode.trim().startsWith(" <" )) {
268+ throw ErrorLoadingException (" Failed: leviathan.js intercepted by Cloudflare. Try disabling your proxy/VPN for a while." )
269+ }
258270
259- if (token.isNullOrEmpty()) {
260- throw ErrorLoadingException (" Failed: Failed to retrieve token from refresh-token endpoint." )
271+ val host = URI (mainUrl).host
272+
273+ val script = """
274+ var window = this;
275+ var global = this;
276+ var document = { createElement: function() { return {}; } };
277+ var navigator = { userAgent: "Mozilla/5.0" };
278+ var location = { hostname: "$host ", href: "$mainUrl " };
279+
280+ var extractedToken = "FAILED_EMPTY";
281+
282+ var fetch = function(reqUrl, options) {
283+ if (options && options.headers && options.headers['Authorization']) {
284+ extractedToken = options.headers['Authorization'];
285+ }
286+ };
287+
288+ var ${' $' } = function(options) {
289+ if (options && options.headers && options.headers['Authorization']) {
290+ extractedToken = options.headers['Authorization'];
291+ }
292+ return { done: function(){ return this; }, fail: function(){ return this; } };
293+ };
294+ ${' $' } .ajax = ${' $' } ;
295+ window.${' $' } = ${' $' } ;
296+ window.jQuery = ${' $' } ;
297+
298+ try {
299+ $jsCode
300+ } catch(e) {
301+ extractedToken = "ERROR_EVAL: " + e.message;
302+ }
303+
304+ if (extractedToken === "FAILED_EMPTY") {
305+ for (var key in window) {
306+ if (typeof window[key] === 'function' && key !== 'fetch' && key !== '${' $' } ' && key !== 'evaluate') {
307+ try {
308+ window[key]('https://dummy', 'GET', "{}");
309+ } catch(e) {}
310+ }
311+ }
312+ }
313+
314+ extractedToken;
315+ """ .trimIndent()
316+
317+ val authHeader = QuickJs .create().use { ctx ->
318+ ctx.evaluate(script) as String?
319+ }
320+
321+ if (authHeader.isNullOrEmpty() || authHeader.startsWith(" FAILED" ) || authHeader.startsWith(" ERROR" )) {
322+ throw ErrorLoadingException (" QuickJs failed to extract token: $authHeader " )
261323 }
262324
263- return token
325+ return authHeader.replace( " Bearer " , " " , ignoreCase = true ).trim()
264326 }
265327
266328 private fun randomId (length : Int = 6): String {
0 commit comments