forked from recloudstream/cloudstream
		
	more links gogo, fixed #503
This commit is contained in:
		
							parent
							
								
									26ba1de2f2
								
							
						
					
					
						commit
						45567fb4f4
					
				
					 4 changed files with 160 additions and 34 deletions
				
			
		|  | @ -267,10 +267,24 @@ abstract class MainAPI { | ||||||
| /** Might need a different implementation for desktop*/ | /** Might need a different implementation for desktop*/ | ||||||
| @SuppressLint("NewApi") | @SuppressLint("NewApi") | ||||||
| fun base64Decode(string: String): String { | fun base64Decode(string: String): String { | ||||||
|  |     return String(base64DecodeArray(string), Charsets.ISO_8859_1) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @SuppressLint("NewApi") | ||||||
|  | fun base64DecodeArray(string: String): ByteArray { | ||||||
|     return try { |     return try { | ||||||
|         String(android.util.Base64.decode(string, android.util.Base64.DEFAULT), Charsets.ISO_8859_1) |         android.util.Base64.decode(string, android.util.Base64.DEFAULT) | ||||||
|     } catch (e: Exception) { |     } catch (e: Exception) { | ||||||
|         String(Base64.getDecoder().decode(string)) |         Base64.getDecoder().decode(string) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @SuppressLint("NewApi") | ||||||
|  | fun base64Encode(array: ByteArray): String { | ||||||
|  |     return try { | ||||||
|  |         String(android.util.Base64.encode(array, android.util.Base64.NO_WRAP), Charsets.ISO_8859_1) | ||||||
|  |     } catch (e: Exception) { | ||||||
|  |         String(Base64.getEncoder().encode(array)) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -32,3 +32,23 @@ fun <T, R> Iterable<T>.pmap( | ||||||
| fun <A, B> List<A>.apmap(f: suspend (A) -> B): List<B> = runBlocking { | fun <A, B> List<A>.apmap(f: suspend (A) -> B): List<B> = runBlocking { | ||||||
|     map { async { f(it) } }.map { it.await() } |     map { async { f(it) } }.map { it.await() } | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | // run code in parallel | ||||||
|  | fun <R> argpmap( | ||||||
|  |     vararg transforms: () -> R, | ||||||
|  |     numThreads: Int = maxOf(Runtime.getRuntime().availableProcessors() - 2, 1), | ||||||
|  |     exec: ExecutorService = Executors.newFixedThreadPool(numThreads) | ||||||
|  | ) { | ||||||
|  |     for (item in transforms) { | ||||||
|  |         exec.submit { item.invoke() } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     exec.shutdown() | ||||||
|  |     exec.awaitTermination(1, TimeUnit.DAYS) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //fun <R> argamap( | ||||||
|  | //    vararg transforms: () -> R, | ||||||
|  | //) = runBlocking { | ||||||
|  | //    transforms.map { async { it.invoke() } }.map { it.await() } | ||||||
|  | //} | ||||||
|  | @ -1,11 +1,15 @@ | ||||||
| package com.lagradost.cloudstream3.animeproviders | package com.lagradost.cloudstream3.animeproviders | ||||||
| 
 | 
 | ||||||
| import com.lagradost.cloudstream3.* | import com.lagradost.cloudstream3.* | ||||||
|  | import com.lagradost.cloudstream3.utils.AppUtils | ||||||
| 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 com.lagradost.cloudstream3.utils.loadExtractor | import com.lagradost.cloudstream3.utils.loadExtractor | ||||||
| import org.jsoup.Jsoup | import org.jsoup.Jsoup | ||||||
| import java.util.* | import java.util.* | ||||||
|  | import javax.crypto.Cipher | ||||||
|  | import javax.crypto.spec.IvParameterSpec | ||||||
|  | import javax.crypto.spec.SecretKeySpec | ||||||
| 
 | 
 | ||||||
| class GogoanimeProvider : MainAPI() { | class GogoanimeProvider : MainAPI() { | ||||||
|     companion object { |     companion object { | ||||||
|  | @ -24,6 +28,26 @@ class GogoanimeProvider : MainAPI() { | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         val qualityRegex = Regex("(\\d+)P") |         val qualityRegex = Regex("(\\d+)P") | ||||||
|  | 
 | ||||||
|  |         // https://github.com/saikou-app/saikou/blob/3e756bd8e876ad7a9318b17110526880525a5cd3/app/src/main/java/ani/saikou/anime/source/extractors/GogoCDN.kt#L60 | ||||||
|  |         // No Licence on the function | ||||||
|  |         private fun cryptoHandler( | ||||||
|  |             string: String, | ||||||
|  |             iv: ByteArray, | ||||||
|  |             secretKeyString: ByteArray, | ||||||
|  |             encrypt: Boolean = true | ||||||
|  |         ): String { | ||||||
|  |             val ivParameterSpec = IvParameterSpec(iv) | ||||||
|  |             val secretKey = SecretKeySpec(secretKeyString, "AES") | ||||||
|  |             val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding") | ||||||
|  |             return if (!encrypt) { | ||||||
|  |                 cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec) | ||||||
|  |                 String(cipher.doFinal(base64DecodeArray(string))) | ||||||
|  |             } else { | ||||||
|  |                 cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivParameterSpec) | ||||||
|  |                 base64Encode(cipher.doFinal(string.toByteArray())) | ||||||
|  |             } | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     override val mainUrl = "https://gogoanime.wiki" |     override val mainUrl = "https://gogoanime.wiki" | ||||||
|  | @ -193,11 +217,28 @@ class GogoanimeProvider : MainAPI() { | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     data class GogoSources( | ||||||
|  |         val source: List<GogoSource>?, | ||||||
|  |         val sourceBk: List<GogoSource>?, | ||||||
|  |         //val track: List<Any?>, | ||||||
|  |         //val advertising: List<Any?>, | ||||||
|  |         //val linkiframe: String | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     data class GogoSource( | ||||||
|  |         val file: String, | ||||||
|  |         val label: String?, | ||||||
|  |         val type: String?, | ||||||
|  |         val default: String? = null | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|     private fun extractVideos(uri: String, callback: (ExtractorLink) -> Unit) { |     private fun extractVideos(uri: String, callback: (ExtractorLink) -> Unit) { | ||||||
|         val doc = app.get(uri).document |         val doc = app.get(uri).document | ||||||
| 
 | 
 | ||||||
|         val iframe = fixUrlNull(doc.selectFirst("div.play-video > iframe").attr("src")) ?: return |         val iframe = fixUrlNull(doc.selectFirst("div.play-video > iframe").attr("src")) ?: return | ||||||
| 
 | 
 | ||||||
|  |         argpmap( | ||||||
|  |             { | ||||||
|                 val link = iframe.replace("streaming.php", "download") |                 val link = iframe.replace("streaming.php", "download") | ||||||
|                 val page = app.get(link, headers = mapOf("Referer" to iframe)) |                 val page = app.get(link, headers = mapOf("Referer" to iframe)) | ||||||
| 
 | 
 | ||||||
|  | @ -205,7 +246,8 @@ class GogoanimeProvider : MainAPI() { | ||||||
|                     if (it.hasAttr("download")) { |                     if (it.hasAttr("download")) { | ||||||
|                         val qual = if (it.text() |                         val qual = if (it.text() | ||||||
|                                 .contains("HDP") |                                 .contains("HDP") | ||||||
|                 ) "1080" else qualityRegex.find(it.text())?.destructured?.component1().toString() |                         ) "1080" else qualityRegex.find(it.text())?.destructured?.component1() | ||||||
|  |                             .toString() | ||||||
|                         callback( |                         callback( | ||||||
|                             ExtractorLink( |                             ExtractorLink( | ||||||
|                                 "Gogoanime", |                                 "Gogoanime", | ||||||
|  | @ -221,15 +263,65 @@ class GogoanimeProvider : MainAPI() { | ||||||
|                         loadExtractor(url, null, callback) |                         loadExtractor(url, null, callback) | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
| 
 |             }, { | ||||||
|                 val streamingResponse = app.get(iframe, headers = mapOf("Referer" to iframe)) |                 val streamingResponse = app.get(iframe, headers = mapOf("Referer" to iframe)) | ||||||
|         streamingResponse.document.select(".list-server-items > .linkserver") |                 val streamingDocument = streamingResponse.document | ||||||
|  |                 argpmap({ | ||||||
|  |                     streamingDocument.select(".list-server-items > .linkserver") | ||||||
|                         ?.forEach { element -> |                         ?.forEach { element -> | ||||||
|                             val status = element.attr("data-status") ?: return@forEach |                             val status = element.attr("data-status") ?: return@forEach | ||||||
|                             if (status != "1") return@forEach |                             if (status != "1") return@forEach | ||||||
|                             val data = element.attr("data-video") ?: return@forEach |                             val data = element.attr("data-video") ?: return@forEach | ||||||
|                             loadExtractor(data, streamingResponse.url, callback) |                             loadExtractor(data, streamingResponse.url, callback) | ||||||
|                         } |                         } | ||||||
|  |                 }, { | ||||||
|  |                     // https://github.com/saikou-app/saikou/blob/3e756bd8e876ad7a9318b17110526880525a5cd3/app/src/main/java/ani/saikou/anime/source/extractors/GogoCDN.kt | ||||||
|  |                     // No Licence on the following code | ||||||
|  |                     val encrypted = | ||||||
|  |                         streamingDocument.select("script[data-name='crypto']").attr("data-value") | ||||||
|  |                     val iv = streamingDocument.select("script[data-name='ts']").attr("data-value") | ||||||
|  |                         .toByteArray() | ||||||
|  | 
 | ||||||
|  |                     val id = Regex("id=([^&]+)").find(iframe)!!.value.removePrefix("id=") | ||||||
|  | 
 | ||||||
|  |                     val secretKey = cryptoHandler(encrypted, iv, iv + iv, false) | ||||||
|  |                     val encryptedId = | ||||||
|  |                         cryptoHandler(id, "0000000000000000".toByteArray(), secretKey.toByteArray()) | ||||||
|  | 
 | ||||||
|  |                     val jsonResponse = | ||||||
|  |                         app.get( | ||||||
|  |                             "http://gogoplay.io/encrypt-ajax.php?id=$encryptedId&time=00000000000000000000", | ||||||
|  |                             headers = mapOf("X-Requested-With" to "XMLHttpRequest") | ||||||
|  |                         ) | ||||||
|  |                     val sources = AppUtils.parseJson<GogoSources>(jsonResponse.text) | ||||||
|  | 
 | ||||||
|  |                     fun invokeGogoSource( | ||||||
|  |                         source: GogoSource, | ||||||
|  |                         sourceCallback: (ExtractorLink) -> Unit | ||||||
|  |                     ) { | ||||||
|  |                         sourceCallback.invoke( | ||||||
|  |                             ExtractorLink( | ||||||
|  |                                 this.name, | ||||||
|  |                                 "${this.name} ${source.label?.replace("0 P","0p") ?: ""}", | ||||||
|  |                                 source.file, | ||||||
|  |                                 "", | ||||||
|  |                                 getQualityFromName(source.label ?: ""), | ||||||
|  |                                 isM3u8 = source.type == "hls" | ||||||
|  |                             ) | ||||||
|  |                         ) | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     sources.source?.forEach { | ||||||
|  |                         println("${this.name} ${it.label ?: ""}") | ||||||
|  |                         invokeGogoSource(it, callback) | ||||||
|  |                     } | ||||||
|  |                     sources.sourceBk?.forEach { | ||||||
|  |                         println("${this.name} ${it.label ?: ""}") | ||||||
|  |                         invokeGogoSource(it, callback) | ||||||
|  |                     } | ||||||
|  |                 }) | ||||||
|  |             } | ||||||
|  |         ) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     override suspend fun loadLinks( |     override suspend fun loadLinks( | ||||||
|  |  | ||||||
|  | @ -52,7 +52,7 @@ enum class Qualities(var value: Int) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fun getQualityFromName(qualityName: String): Int { | fun getQualityFromName(qualityName: String): Int { | ||||||
|     return when (qualityName.replace("p", "").replace("P", "")) { |     return when (qualityName.replace("p", "").replace("P", "").trim()) { | ||||||
|         "360" -> Qualities.P360 |         "360" -> Qualities.P360 | ||||||
|         "480" -> Qualities.P480 |         "480" -> Qualities.P480 | ||||||
|         "720" -> Qualities.P720 |         "720" -> Qualities.P720 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue