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*/ | ||||
| @SuppressLint("NewApi") | ||||
| fun base64Decode(string: String): String { | ||||
|     return String(base64DecodeArray(string), Charsets.ISO_8859_1) | ||||
| } | ||||
| 
 | ||||
| @SuppressLint("NewApi") | ||||
| fun base64DecodeArray(string: String): ByteArray { | ||||
|     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) { | ||||
|         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)) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -29,6 +29,26 @@ fun <T, R> Iterable<T>.pmap( | |||
|     return ArrayList<R>(destination) | ||||
| } | ||||
| 
 | ||||
| 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() } | ||||
| } | ||||
| } | ||||
| 
 | ||||
| // 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 | ||||
| 
 | ||||
| import com.lagradost.cloudstream3.* | ||||
| import com.lagradost.cloudstream3.utils.AppUtils | ||||
| import com.lagradost.cloudstream3.utils.ExtractorLink | ||||
| import com.lagradost.cloudstream3.utils.getQualityFromName | ||||
| import com.lagradost.cloudstream3.utils.loadExtractor | ||||
| import org.jsoup.Jsoup | ||||
| import java.util.* | ||||
| import javax.crypto.Cipher | ||||
| import javax.crypto.spec.IvParameterSpec | ||||
| import javax.crypto.spec.SecretKeySpec | ||||
| 
 | ||||
| class GogoanimeProvider : MainAPI() { | ||||
|     companion object { | ||||
|  | @ -24,6 +28,26 @@ class GogoanimeProvider : MainAPI() { | |||
|         } | ||||
| 
 | ||||
|         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" | ||||
|  | @ -193,43 +217,111 @@ 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) { | ||||
|         val doc = app.get(uri).document | ||||
| 
 | ||||
|         val iframe = fixUrlNull(doc.selectFirst("div.play-video > iframe").attr("src")) ?: return | ||||
| 
 | ||||
|         val link = iframe.replace("streaming.php", "download") | ||||
|         val page = app.get(link, headers = mapOf("Referer" to iframe)) | ||||
|         argpmap( | ||||
|             { | ||||
|                 val link = iframe.replace("streaming.php", "download") | ||||
|                 val page = app.get(link, headers = mapOf("Referer" to iframe)) | ||||
| 
 | ||||
|         page.document.select(".dowload > a").pmap { | ||||
|             if (it.hasAttr("download")) { | ||||
|                 val qual = if (it.text() | ||||
|                         .contains("HDP") | ||||
|                 ) "1080" else qualityRegex.find(it.text())?.destructured?.component1().toString() | ||||
|                 callback( | ||||
|                     ExtractorLink( | ||||
|                         "Gogoanime", | ||||
|                         if (qual == "null") "Gogoanime" else "Gogoanime - " + qual + "p", | ||||
|                         it.attr("href"), | ||||
|                         page.url, | ||||
|                         getQualityFromName(qual), | ||||
|                         it.attr("href").contains(".m3u8") | ||||
|                     ) | ||||
|                 ) | ||||
|             } else { | ||||
|                 val url = it.attr("href") | ||||
|                 loadExtractor(url, null, callback) | ||||
|             } | ||||
|         } | ||||
|                 page.document.select(".dowload > a").pmap { | ||||
|                     if (it.hasAttr("download")) { | ||||
|                         val qual = if (it.text() | ||||
|                                 .contains("HDP") | ||||
|                         ) "1080" else qualityRegex.find(it.text())?.destructured?.component1() | ||||
|                             .toString() | ||||
|                         callback( | ||||
|                             ExtractorLink( | ||||
|                                 "Gogoanime", | ||||
|                                 if (qual == "null") "Gogoanime" else "Gogoanime - " + qual + "p", | ||||
|                                 it.attr("href"), | ||||
|                                 page.url, | ||||
|                                 getQualityFromName(qual), | ||||
|                                 it.attr("href").contains(".m3u8") | ||||
|                             ) | ||||
|                         ) | ||||
|                     } else { | ||||
|                         val url = it.attr("href") | ||||
|                         loadExtractor(url, null, callback) | ||||
|                     } | ||||
|                 } | ||||
|             }, { | ||||
|                 val streamingResponse = app.get(iframe, headers = mapOf("Referer" to iframe)) | ||||
|                 val streamingDocument = streamingResponse.document | ||||
|                 argpmap({ | ||||
|                     streamingDocument.select(".list-server-items > .linkserver") | ||||
|                         ?.forEach { element -> | ||||
|                             val status = element.attr("data-status") ?: return@forEach | ||||
|                             if (status != "1") return@forEach | ||||
|                             val data = element.attr("data-video") ?: return@forEach | ||||
|                             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 streamingResponse = app.get(iframe, headers = mapOf("Referer" to iframe)) | ||||
|         streamingResponse.document.select(".list-server-items > .linkserver") | ||||
|             ?.forEach { element -> | ||||
|                 val status = element.attr("data-status") ?: return@forEach | ||||
|                 if (status != "1") return@forEach | ||||
|                 val data = element.attr("data-video") ?: return@forEach | ||||
|                 loadExtractor(data, streamingResponse.url, callback) | ||||
|                     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( | ||||
|  |  | |||
|  | @ -52,7 +52,7 @@ enum class Qualities(var value: Int) { | |||
| } | ||||
| 
 | ||||
| fun getQualityFromName(qualityName: String): Int { | ||||
|     return when (qualityName.replace("p", "").replace("P", "")) { | ||||
|     return when (qualityName.replace("p", "").replace("P", "").trim()) { | ||||
|         "360" -> Qualities.P360 | ||||
|         "480" -> Qualities.P480 | ||||
|         "720" -> Qualities.P720 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue