mirror of
				https://github.com/hexated/cloudstream-extensions-hexated.git
				synced 2024-08-15 00:03:22 +00:00 
			
		
		
		
	updated Stremio
This commit is contained in:
		
							parent
							
								
									6299dedf58
								
							
						
					
					
						commit
						a1f47f1dc6
					
				
					 3 changed files with 261 additions and 1 deletions
				
			
		|  | @ -6,7 +6,7 @@ cloudstream { | |||
|     language = "en" | ||||
|     // All of these properties are optional, you can safely remove them | ||||
| 
 | ||||
|      description = "Allow you to use Stremio addons as sources such as torrentio. (!) Requires setup" | ||||
|      description = "- Stremiox allows you to use stream addons \n- Stremio allows you to use catalog addons \n<!> Requires Setup" | ||||
|      authors = listOf("Hexated") | ||||
| 
 | ||||
|     /** | ||||
|  |  | |||
							
								
								
									
										259
									
								
								StremioX/src/main/kotlin/com/hexated/Stremio.kt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										259
									
								
								StremioX/src/main/kotlin/com/hexated/Stremio.kt
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,259 @@ | |||
| package com.hexated | ||||
| 
 | ||||
| import android.util.Log | ||||
| import com.lagradost.cloudstream3.* | ||||
| import com.lagradost.cloudstream3.utils.* | ||||
| import com.lagradost.cloudstream3.utils.AppUtils.parseJson | ||||
| import com.lagradost.cloudstream3.utils.AppUtils.toJson | ||||
| import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson | ||||
| import org.json.JSONObject | ||||
| import java.net.URI | ||||
| 
 | ||||
| private const val TRACKER_LIST_URL = | ||||
|     "https://raw.githubusercontent.com/ngosang/trackerslist/master/trackers_best.txt" | ||||
| 
 | ||||
| class Stremio : MainAPI() { | ||||
|     override var mainUrl = "https://stremio.github.io/stremio-static-addon-example" | ||||
|     override var name = "Stremio" | ||||
|     override val supportedTypes = setOf(TvType.Others) | ||||
|     override val hasMainPage = true | ||||
| 
 | ||||
|     override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse? { | ||||
|         val res = tryParseJson<Manifest>(app.get("${mainUrl}/manifest.json").text) ?: return null | ||||
|         val lists = mutableListOf<HomePageList>() | ||||
|         res.catalogs.forEach { catalog -> | ||||
|             catalog.toHomePageList(this)?.let { | ||||
|                 lists.add(it) | ||||
|             } | ||||
|         } | ||||
|         return HomePageResponse( | ||||
|             lists, | ||||
|             false | ||||
|         ) | ||||
|     } | ||||
| 
 | ||||
|     override suspend fun search(query: String): List<SearchResponse>? { | ||||
|         val res = tryParseJson<Manifest>(app.get("${mainUrl}/manifest.json").text) ?: return null | ||||
|         val list = mutableListOf<SearchResponse>() | ||||
|         res.catalogs.forEach { catalog -> | ||||
|             list.addAll(catalog.search(query, this)) | ||||
|         } | ||||
|         return list | ||||
|     } | ||||
| 
 | ||||
|     override suspend fun load(url: String): LoadResponse? { | ||||
|         val res = parseJson<CatalogEntry>(url) | ||||
|         val json = app.get("${mainUrl}/meta/${res.type}/${res.id}.json") | ||||
|             .parsedSafe<CatalogResponse>()?.meta ?: throw RuntimeException(url) | ||||
|         return json.toLoadResponse(this) | ||||
|     } | ||||
| 
 | ||||
|     override suspend fun loadLinks( | ||||
|         data: String, | ||||
|         isCasting: Boolean, | ||||
|         subtitleCallback: (SubtitleFile) -> Unit, | ||||
|         callback: (ExtractorLink) -> Unit | ||||
|     ): Boolean { | ||||
|         val res = tryParseJson<StreamsResponse>(app.get(data).text) ?: return false | ||||
|         res.streams.forEach { stream -> | ||||
|             stream.runCallback(this, subtitleCallback, callback) | ||||
|         } | ||||
| 
 | ||||
|         return true | ||||
|     } | ||||
| 
 | ||||
|     private data class Manifest(val catalogs: List<Catalog>) | ||||
|     private data class Catalog( | ||||
|         var name: String?, | ||||
|         val id: String, | ||||
|         val type: String?, | ||||
|         val types: MutableList<String> = mutableListOf() | ||||
|     ) { | ||||
|         init { | ||||
|             if (type != null) types.add(type) | ||||
|         } | ||||
| 
 | ||||
|         suspend fun search(query: String, provider: Stremio): List<SearchResponse> { | ||||
|             val entries = mutableListOf<SearchResponse>() | ||||
|             types.forEach { type -> | ||||
|                 val json = | ||||
|                     app.get("${provider.mainUrl}/catalog/${type}/${id}/search=${query}.json").text | ||||
|                 val res = | ||||
|                     tryParseJson<CatalogResponse>(json) | ||||
|                         ?: return@forEach | ||||
|                 res.metas?.forEach { entry -> | ||||
|                     entries.add(entry.toSearchResponse(provider)) | ||||
|                 } | ||||
|             } | ||||
|             return entries | ||||
|         } | ||||
| 
 | ||||
|         suspend fun toHomePageList(provider: Stremio): HomePageList? { | ||||
|             val entries = mutableListOf<SearchResponse>() | ||||
|             types.forEach { type -> | ||||
|                 val json = app.get("${provider.mainUrl}/catalog/${type}/${id}.json").text | ||||
|                 val res = | ||||
|                     tryParseJson<CatalogResponse>(json) | ||||
|                         ?: return@forEach | ||||
|                 res.metas?.forEach { entry -> | ||||
|                     entries.add(entry.toSearchResponse(provider)) | ||||
|                 } | ||||
|             } | ||||
|             return HomePageList( | ||||
|                 name ?: id, | ||||
|                 entries | ||||
|             ) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private data class CatalogResponse(val metas: List<CatalogEntry>?, val meta: CatalogEntry?) | ||||
|     private data class CatalogEntry( | ||||
|         val name: String, | ||||
|         val id: String, | ||||
|         val poster: String?, | ||||
|         val description: String?, | ||||
|         val type: String?, | ||||
|         val videos: List<Video>? | ||||
|     ) { | ||||
|         fun toSearchResponse(provider: Stremio): SearchResponse { | ||||
|             return provider.newMovieSearchResponse( | ||||
|                 fixTitle(name), | ||||
|                 this.toJson(), | ||||
|                 TvType.Others | ||||
|             ) { | ||||
|                 posterUrl = poster | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         suspend fun toLoadResponse(provider: Stremio): LoadResponse { | ||||
|             if (videos == null || videos.isEmpty()) { | ||||
|                 return provider.newMovieLoadResponse( | ||||
|                     name, | ||||
|                     "${provider.mainUrl}/meta/${type}/${id}.json", | ||||
|                     TvType.Others, | ||||
|                     "${provider.mainUrl}/stream/${type}/${id}.json" | ||||
|                 ) { | ||||
|                     posterUrl = poster | ||||
|                     plot = description | ||||
|                 } | ||||
|             } else { | ||||
|                 return provider.newTvSeriesLoadResponse( | ||||
|                     name, | ||||
|                     "${provider.mainUrl}/meta/${type}/${id}.json", | ||||
|                     TvType.Others, | ||||
|                     videos.map { | ||||
|                         it.toEpisode(provider, type) | ||||
|                     } | ||||
|                 ) { | ||||
|                     posterUrl = poster | ||||
|                     plot = description | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private data class Video( | ||||
|         val id: String, | ||||
|         val title: String?, | ||||
|         val thumbnail: String?, | ||||
|         val overview: String? | ||||
|     ) { | ||||
|         fun toEpisode(provider: Stremio, type: String?): Episode { | ||||
|             return provider.newEpisode( | ||||
|                 "${provider.mainUrl}/stream/${type}/${id}.json" | ||||
|             ) { | ||||
|                 this.name = title | ||||
|                 this.posterUrl = thumbnail | ||||
|                 this.description = overview | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private data class StreamsResponse(val streams: List<Stream>) | ||||
|     private data class Subtitle( | ||||
|         val url: String?, | ||||
|         val lang: String?, | ||||
|         val id: String?, | ||||
|     ) | ||||
| 
 | ||||
|     private data class Stream( | ||||
|         val name: String?, | ||||
|         val title: String?, | ||||
|         val url: String?, | ||||
|         val description: String?, | ||||
|         val ytId: String?, | ||||
|         val externalUrl: String?, | ||||
|         val behaviorHints: JSONObject?, | ||||
|         val infoHash: String?, | ||||
|         val sources: List<String> = emptyList(), | ||||
|         val subtitles: List<Subtitle> = emptyList() | ||||
|     ) { | ||||
|         suspend fun runCallback( | ||||
|             provider: Stremio, | ||||
|             subtitleCallback: (SubtitleFile) -> Unit, | ||||
|             callback: (ExtractorLink) -> Unit | ||||
|         ) { | ||||
|             if (url != null) { | ||||
|                 var referer: String? = null | ||||
|                 try { | ||||
|                     val headers = ((behaviorHints?.get("proxyHeaders") as? JSONObject) | ||||
|                         ?.get("request") as? JSONObject) | ||||
|                     referer = | ||||
|                         headers?.get("referer") as? String ?: headers?.get("origin") as? String | ||||
|                 } catch (ex: Throwable) { | ||||
|                     Log.e("Stremio", Log.getStackTraceString(ex)) | ||||
|                 } | ||||
|                 callback.invoke( | ||||
|                     ExtractorLink( | ||||
|                         name ?: "", | ||||
|                         title ?: name ?: "", | ||||
|                         url, | ||||
|                         if (provider.mainUrl.contains("kisskh")) "https://kisskh.me/" else referer | ||||
|                             ?: "", | ||||
|                         getQualityFromName(description), | ||||
|                         isM3u8 = URI(url).path.endsWith(".m3u8") | ||||
|                     ) | ||||
|                 ) | ||||
|                 subtitles.map { sub -> | ||||
|                     subtitleCallback.invoke( | ||||
|                         SubtitleFile( | ||||
|                             SubtitleHelper.fromThreeLettersToLanguage(sub.lang ?: "") ?: sub.lang | ||||
|                             ?: "", | ||||
|                             sub.url ?: return@map | ||||
|                         ) | ||||
|                     ) | ||||
|                 } | ||||
|             } | ||||
|             if (ytId != null) { | ||||
|                 loadExtractor("https://www.youtube.com/watch?v=$ytId", subtitleCallback, callback) | ||||
|             } | ||||
|             if (externalUrl != null) { | ||||
|                 loadExtractor(externalUrl, subtitleCallback, callback) | ||||
|             } | ||||
|             if (infoHash != null) { | ||||
|                 val resp = app.get(TRACKER_LIST_URL).text | ||||
|                 val otherTrackers = resp | ||||
|                     .split("\n") | ||||
|                     .filterIndexed { i, s -> i % 2 == 0 } | ||||
|                     .filter { s -> s.isNotEmpty() }.joinToString("") { "&tr=$it" } | ||||
| 
 | ||||
|                 val sourceTrackers = sources | ||||
|                     .filter { it.startsWith("tracker:") } | ||||
|                     .map { it.removePrefix("tracker:") } | ||||
|                     .filter { s -> s.isNotEmpty() }.joinToString("") { "&tr=$it" } | ||||
| 
 | ||||
|                 val magnet = "magnet:?xt=urn:btih:${infoHash}${sourceTrackers}${otherTrackers}" | ||||
|                 callback.invoke( | ||||
|                     ExtractorLink( | ||||
|                         name ?: "", | ||||
|                         title ?: name ?: "", | ||||
|                         magnet, | ||||
|                         "", | ||||
|                         Qualities.Unknown.value | ||||
|                     ) | ||||
|                 ) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -10,5 +10,6 @@ class StremioXPlugin: Plugin() { | |||
|     override fun load(context: Context) { | ||||
|         // All providers should be added in this manner. Please don't edit the providers list directly. | ||||
|         registerMainAPI(StremioX()) | ||||
|         registerMainAPI(Stremio()) | ||||
|     } | ||||
| } | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue