mirror of
				https://github.com/hexated/cloudstream-extensions-hexated.git
				synced 2024-08-15 00:03:22 +00:00 
			
		
		
		
	added 123tv
This commit is contained in:
		
							parent
							
								
									43e6df19ab
								
							
						
					
					
						commit
						85f2cd7dd0
					
				
					 4 changed files with 215 additions and 0 deletions
				
			
		
							
								
								
									
										25
									
								
								OnetwothreeTv/build.gradle.kts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								OnetwothreeTv/build.gradle.kts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,25 @@ | |||
| // use an integer for version numbers | ||||
| version = 1 | ||||
| 
 | ||||
| 
 | ||||
| cloudstream { | ||||
|     language = "en" | ||||
|     // All of these properties are optional, you can safely remove them | ||||
| 
 | ||||
|     // description = "Lorem Ipsum" | ||||
|     authors = listOf("Hexated") | ||||
| 
 | ||||
|     /** | ||||
|      * Status int as the following: | ||||
|      * 0: Down | ||||
|      * 1: Ok | ||||
|      * 2: Slow | ||||
|      * 3: Beta only | ||||
|      * */ | ||||
|     status = 1 // will be 3 if unspecified | ||||
|     tvTypes = listOf( | ||||
|         "Live", | ||||
|     ) | ||||
| 
 | ||||
|     iconUrl = "https://www.google.com/s2/favicons?domain=123tv.live&sz=%size%" | ||||
| } | ||||
							
								
								
									
										2
									
								
								OnetwothreeTv/src/main/AndroidManifest.xml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								OnetwothreeTv/src/main/AndroidManifest.xml
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,2 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <manifest package="com.hexated"/> | ||||
							
								
								
									
										174
									
								
								OnetwothreeTv/src/main/kotlin/com/hexated/OnetwothreeTv.kt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										174
									
								
								OnetwothreeTv/src/main/kotlin/com/hexated/OnetwothreeTv.kt
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,174 @@ | |||
| package com.hexated | ||||
| 
 | ||||
| import com.fasterxml.jackson.annotation.JsonProperty | ||||
| import com.lagradost.cloudstream3.* | ||||
| import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson | ||||
| import com.lagradost.cloudstream3.utils.ExtractorLink | ||||
| import com.lagradost.cloudstream3.utils.M3u8Helper | ||||
| import com.lagradost.cloudstream3.utils.Qualities | ||||
| import org.jsoup.nodes.Element | ||||
| import javax.crypto.Cipher | ||||
| import javax.crypto.SecretKeyFactory | ||||
| import javax.crypto.spec.IvParameterSpec | ||||
| import javax.crypto.spec.PBEKeySpec | ||||
| import javax.crypto.spec.SecretKeySpec | ||||
| 
 | ||||
| class OnetwothreeTv : MainAPI() { | ||||
|     override var mainUrl = "http://123tv.live" | ||||
|     override var name = "123tv" | ||||
|     override val hasDownloadSupport = false | ||||
|     override val hasMainPage = true | ||||
|     override val supportedTypes = setOf( | ||||
|         TvType.Live | ||||
|     ) | ||||
| 
 | ||||
|     override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse { | ||||
|         val homePageList = ArrayList<HomePageList>() | ||||
|         listOf( | ||||
|             Pair("$mainUrl/category/united-states-usa/", "United States (USA)"), | ||||
|             Pair("$mainUrl/top-streams/", "Top Streams"), | ||||
|             Pair("$mainUrl/latest-streams/", "Latest Streams") | ||||
|         ).apmap { | ||||
|             val home = | ||||
|                 app.get(it.first).document.select("div.videos-latest-list.row div.col-md-3.col-sm-6") | ||||
|                     .mapNotNull { item -> | ||||
|                         item.toSearchResult() | ||||
|                     } | ||||
|             if (home.isNotEmpty()) homePageList.add(HomePageList(it.second, home, true)) | ||||
|         } | ||||
|         return HomePageResponse(homePageList) | ||||
|     } | ||||
| 
 | ||||
|     private fun Element.toSearchResult(): LiveSearchResponse? { | ||||
|         return LiveSearchResponse( | ||||
|             this.selectFirst("div.video-title h4")?.text() ?: return null, | ||||
|             fixUrl(this.selectFirst("a")!!.attr("href")), | ||||
|             this@OnetwothreeTv.name, | ||||
|             TvType.Live, | ||||
|             fixUrlNull(this.selectFirst("img")?.attr("src")), | ||||
|         ) | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     override suspend fun search(query: String): List<SearchResponse> { | ||||
|         return app.get( | ||||
|             "$mainUrl/?s=$query" | ||||
|         ).document.select("div.videos-latest-list.row div.col-md-3.col-sm-6").mapNotNull { | ||||
|             it.toSearchResult() | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     override suspend fun load(url: String): LoadResponse? { | ||||
|         val document = app.get(url).document | ||||
|         return LiveStreamLoadResponse( | ||||
|             document.selectFirst("div.video-big-title h1")?.text() ?: return null, | ||||
|             url, | ||||
|             this.name, | ||||
|             document.selectFirst("div.embed-responsive iframe")?.attr("src") ?: url, | ||||
|             fixUrlNull(document.selectFirst("meta[name=\"twitter:image\"]")?.attr("content")), | ||||
|             plot = document.select("div.watch-video-description p").text() ?: return null | ||||
|         ) | ||||
|     } | ||||
| 
 | ||||
|     private fun String.decodeHex(): ByteArray { | ||||
|         check(length % 2 == 0) { "Must have an even length" } | ||||
|         return chunked(2) | ||||
|             .map { it.toInt(16).toByte() } | ||||
|             .toByteArray() | ||||
|     } | ||||
| 
 | ||||
|     private fun cryptojsAESHandler( | ||||
|         data: AesData, | ||||
|         pass: String, | ||||
|         encrypt: Boolean = true | ||||
|     ): String { | ||||
|         val factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512") | ||||
|         val spec = PBEKeySpec(pass.toCharArray(), data.s.decodeHex(), 999, 256) | ||||
|         val key = factory.generateSecret(spec) | ||||
|         val cipher = Cipher.getInstance("AES/CBC/NoPadding") | ||||
|         return if (!encrypt) { | ||||
|             cipher.init( | ||||
|                 Cipher.DECRYPT_MODE, | ||||
|                 SecretKeySpec(key.encoded, "AES"), | ||||
|                 IvParameterSpec(data.iv.decodeHex()) | ||||
|             ) | ||||
|             String(cipher.doFinal(base64DecodeArray(data.ct))) | ||||
|         } else { | ||||
|             cipher.init( | ||||
|                 Cipher.ENCRYPT_MODE, | ||||
|                 SecretKeySpec(key.encoded, "AES"), | ||||
|                 IvParameterSpec(data.iv.decodeHex()) | ||||
|             ) | ||||
|             base64Encode(cipher.doFinal(data.ct.toByteArray())) | ||||
| 
 | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     override suspend fun loadLinks( | ||||
|         data: String, | ||||
|         isCasting: Boolean, | ||||
|         subtitleCallback: (SubtitleFile) -> Unit, | ||||
|         callback: (ExtractorLink) -> Unit | ||||
|     ): Boolean { | ||||
|         if (data.contains(".m3u8")) { | ||||
|             app.get( | ||||
|                 data, | ||||
|                 referer = "$mainUrl/" | ||||
|             ).document.select("script").find { it.data().contains("var player=") }?.data() | ||||
|                 ?.substringAfter("source:'")?.substringBefore("',")?.let { link -> | ||||
|                     callback.invoke( | ||||
|                         ExtractorLink( | ||||
|                             source = name, | ||||
|                             name = name, | ||||
|                             url = link, | ||||
|                             referer = "http://azureedge.xyz/", | ||||
|                             quality = Qualities.Unknown.value, | ||||
|                             isM3u8 = true, | ||||
|                             headers = mapOf("Origin" to "http://azureedge.xyz") | ||||
|                         ) | ||||
|                     ) | ||||
|                 } | ||||
|         } else { | ||||
|             val script = | ||||
|                 app.get(data).document.select("script").find { it.data().contains("var post_id") } | ||||
|                     ?.data() ?: throw ErrorLoadingException("No data found") | ||||
|             val encodeData = | ||||
|                 Regex("\\w{6,10}=\\[(\\S+)];return").find(script)?.groupValues?.getOrNull(1) | ||||
|                     ?.split(",")?.joinToString("") { it.replace("'", "") } | ||||
|                     .let { base64Decode("$it") } | ||||
|             val aesData = tryParseJson<AesData>(encodeData) | ||||
|                 ?: throw ErrorLoadingException("Invalid json responses") | ||||
|             val pass = Regex("\\[((\\d{2,3},?\\s?){4})];").findAll(script).map { it.groupValues[1] } | ||||
|                 .toList().flatMap { it.split(",") }.map { it.toInt().toChar() }.reversed() | ||||
|                 .joinToString("") | ||||
|             val decryptData = cryptojsAESHandler(aesData, pass, false) | ||||
|             val jsonData = Regex("[\"|'](\\?1&json=\\S+)[\"|'];").find(script)?.groupValues?.get(1) | ||||
|             val m3uLink = "${decryptData.substringBefore(".m3u8")}.m3u8$jsonData" | ||||
|             app.get(m3uLink, referer = data).let { | ||||
|                 tryParseJson<List<Source>>(it.text)?.map { res -> | ||||
|                     M3u8Helper.generateM3u8( | ||||
|                         this.name, | ||||
|                         res.file, | ||||
|                         "$mainUrl/", | ||||
|                         headers = mapOf("Origin" to mainUrl) | ||||
|                     ).forEach(callback) | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
|         return true | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     data class Source( | ||||
|         @JsonProperty("file") val file: String, | ||||
|     ) | ||||
| 
 | ||||
|     data class AesData( | ||||
|         @JsonProperty("ciphertext") val ct: String, | ||||
|         @JsonProperty("salt") val s: String, | ||||
|         @JsonProperty("iv") val iv: String, | ||||
|         @JsonProperty("iterations") val iterations: Int? = null, | ||||
|     ) | ||||
| } | ||||
|  | @ -0,0 +1,14 @@ | |||
| 
 | ||||
| package com.hexated | ||||
| 
 | ||||
| import com.lagradost.cloudstream3.plugins.CloudstreamPlugin | ||||
| import com.lagradost.cloudstream3.plugins.Plugin | ||||
| import android.content.Context | ||||
| 
 | ||||
| @CloudstreamPlugin | ||||
| class OnetwothreeTvPlugin: Plugin() { | ||||
|     override fun load(context: Context) { | ||||
|         // All providers should be added in this manner. Please don't edit the providers list directly. | ||||
|         registerMainAPI(OnetwothreeTv()) | ||||
|     } | ||||
| } | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue