mirror of
				https://github.com/hexated/cloudstream-extensions-hexated.git
				synced 2024-08-15 00:03:22 +00:00 
			
		
		
		
	stremiox: make possible stream through catalog addons
This commit is contained in:
		
							parent
							
								
									ba1fbac2a2
								
							
						
					
					
						commit
						3f97c0a253
					
				
					 4 changed files with 190 additions and 115 deletions
				
			
		|  | @ -1,5 +1,5 @@ | |||
| // use an integer for version numbers | ||||
| version = 5 | ||||
| version = 6 | ||||
| 
 | ||||
| 
 | ||||
| cloudstream { | ||||
|  |  | |||
|  | @ -26,7 +26,7 @@ class StremioC : MainAPI() { | |||
|         val lists = mutableListOf<HomePageList>() | ||||
|         res.catalogs.forEach { catalog -> | ||||
|             catalog.toHomePageList(this)?.let { | ||||
|                 if(it.list.isNotEmpty()) lists.add(it) | ||||
|                 if (it.list.isNotEmpty()) lists.add(it) | ||||
|             } | ||||
|         } | ||||
|         return HomePageResponse( | ||||
|  | @ -47,10 +47,11 @@ class StremioC : MainAPI() { | |||
| 
 | ||||
|     override suspend fun load(url: String): LoadResponse? { | ||||
|         val res = parseJson<CatalogEntry>(url) | ||||
|         mainUrl = if((res.type == "movie" || res.type == "series") && isImdborTmdb(res.id)) cinemataUrl else mainUrl | ||||
|         mainUrl = | ||||
|             if ((res.type == "movie" || res.type == "series") && isImdborTmdb(res.id)) cinemataUrl else mainUrl | ||||
|         val json = app.get("${mainUrl}/meta/${res.type}/${res.id}.json") | ||||
|             .parsedSafe<CatalogResponse>()?.meta ?: throw RuntimeException(url) | ||||
|         return json.toLoadResponse(this) | ||||
|         return json.toLoadResponse(this, res.id) | ||||
|     } | ||||
| 
 | ||||
|     override suspend fun loadLinks( | ||||
|  | @ -59,14 +60,74 @@ class StremioC : MainAPI() { | |||
|         subtitleCallback: (SubtitleFile) -> Unit, | ||||
|         callback: (ExtractorLink) -> Unit | ||||
|     ): Boolean { | ||||
|         val res = tryParseJson<StreamsResponse>(app.get(data).text) ?: return false | ||||
|         val loadData = parseJson<LoadData>(data) | ||||
|         val request = app.get("${mainUrl}/stream/${loadData.type}/${loadData.id}.json") | ||||
|         if (request.isSuccessful) { | ||||
|             val res = tryParseJson<StreamsResponse>(request.text) ?: return false | ||||
|             res.streams.forEach { stream -> | ||||
|                 stream.runCallback(this, subtitleCallback, callback) | ||||
|             } | ||||
|         } else { | ||||
|             argamap( | ||||
|                 { | ||||
|                     invokeStremioX(loadData.type, loadData.id, subtitleCallback, callback) | ||||
|                 }, | ||||
|                 { | ||||
|                     SubsExtractors.invokeWatchsomuch( | ||||
|                         loadData.imdbId, | ||||
|                         loadData.season, | ||||
|                         loadData.episode, | ||||
|                         subtitleCallback | ||||
|                     ) | ||||
|                 }, | ||||
|                 { | ||||
|                     SubsExtractors.invokeOpenSubs( | ||||
|                         loadData.imdbId, | ||||
|                         loadData.season, | ||||
|                         loadData.episode, | ||||
|                         subtitleCallback | ||||
|                     ) | ||||
|                 }, | ||||
|             ) | ||||
|         } | ||||
| 
 | ||||
|         return true | ||||
|     } | ||||
| 
 | ||||
|     private suspend fun invokeStremioX( | ||||
|         type: String?, | ||||
|         id: String?, | ||||
|         subtitleCallback: (SubtitleFile) -> Unit, | ||||
|         callback: (ExtractorLink) -> Unit | ||||
|     ) { | ||||
|         val sites = | ||||
|             AcraApplication.getKey<Array<CustomSite>>(USER_PROVIDER_API)?.toMutableList() | ||||
|                 ?: mutableListOf() | ||||
|         sites.filter { it.parentJavaClass == "StremioX" }.apmap { site -> | ||||
|             val res = | ||||
|                 tryParseJson<StreamsResponse>(app.get("${site.url.fixSourceUrl()}/stream/${type}/${id}.json").text) | ||||
|                     ?: return@apmap | ||||
|             res.streams.forEach { stream -> | ||||
|                 stream.runCallback(this, subtitleCallback, callback) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     data class LoadData( | ||||
|         val type: String? = null, | ||||
|         val id: String? = null, | ||||
|         val season: Int? = null, | ||||
|         val episode: Int? = null, | ||||
|         val imdbId: String? = null, | ||||
|     ) | ||||
| 
 | ||||
|     data class CustomSite( | ||||
|         @JsonProperty("parentJavaClass") val parentJavaClass: String, | ||||
|         @JsonProperty("name") val name: String, | ||||
|         @JsonProperty("url") val url: String, | ||||
|         @JsonProperty("lang") val lang: String, | ||||
|     ) | ||||
| 
 | ||||
|     // check if id is imdb/tmdb cause stremio addons like torrentio works base on imdbId | ||||
|     private fun isImdborTmdb(url: String?): Boolean { | ||||
|         return imdbUrlToIdNullable(url) != null || url?.startsWith("tmdb:") == true | ||||
|  | @ -137,13 +198,13 @@ class StremioC : MainAPI() { | |||
|             } | ||||
|         } | ||||
| 
 | ||||
|         suspend fun toLoadResponse(provider: StremioC): LoadResponse { | ||||
|         suspend fun toLoadResponse(provider: StremioC, imdbId: String?): LoadResponse { | ||||
|             if (videos == null || videos.isEmpty()) { | ||||
|                 return provider.newMovieLoadResponse( | ||||
|                     name, | ||||
|                     "${provider.mainUrl}/meta/${type}/${id}.json", | ||||
|                     TvType.Movie, | ||||
|                     "${provider.mainUrl}/stream/${type}/${id}.json" | ||||
|                     LoadData(type, id, imdbId = imdbId) | ||||
|                 ) { | ||||
|                     posterUrl = poster | ||||
|                     backgroundPosterUrl = background | ||||
|  | @ -156,7 +217,7 @@ class StremioC : MainAPI() { | |||
|                     "${provider.mainUrl}/meta/${type}/${id}.json", | ||||
|                     TvType.TvSeries, | ||||
|                     videos.map { | ||||
|                         it.toEpisode(provider, type) | ||||
|                         it.toEpisode(provider, type, imdbId) | ||||
|                     } | ||||
|                 ) { | ||||
|                     posterUrl = poster | ||||
|  | @ -180,9 +241,9 @@ class StremioC : MainAPI() { | |||
|         @JsonProperty("overview") val overview: String? = null, | ||||
|         @JsonProperty("description") val description: String? = null, | ||||
|     ) { | ||||
|         fun toEpisode(provider: StremioC, type: String?): Episode { | ||||
|         fun toEpisode(provider: StremioC, type: String?, imdbId: String?): Episode { | ||||
|             return provider.newEpisode( | ||||
|                 "${provider.mainUrl}/stream/${type}/${id}.json" | ||||
|                 LoadData(type, id, seasonNumber, episode ?: number, imdbId) | ||||
|             ) { | ||||
|                 this.name = name ?: title | ||||
|                 this.posterUrl = thumbnail | ||||
|  |  | |||
|  | @ -2,6 +2,8 @@ package com.hexated | |||
| 
 | ||||
| import android.util.Log | ||||
| import com.fasterxml.jackson.annotation.JsonProperty | ||||
| import com.hexated.SubsExtractors.invokeOpenSubs | ||||
| import com.hexated.SubsExtractors.invokeWatchsomuch | ||||
| import com.lagradost.cloudstream3.* | ||||
| import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer | ||||
| import com.lagradost.cloudstream3.utils.* | ||||
|  | @ -245,72 +247,6 @@ open class StremioX : MainAPI() { | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private suspend fun invokeOpenSubs( | ||||
|         imdbId: String? = null, | ||||
|         season: Int? = null, | ||||
|         episode: Int? = null, | ||||
|         subtitleCallback: (SubtitleFile) -> Unit, | ||||
|     ) { | ||||
|         val id = if(season == null) { | ||||
|             imdbId | ||||
|         } else { | ||||
|             "$imdbId $season $episode" | ||||
|         } | ||||
|         val data = base64Encode("""{"id":1,"jsonrpc":"2.0","method":"subtitles.find","params":[null,{"query":{"itemHash":"$id"}}]}""".toByteArray()) | ||||
|         app.get("$openSubAPI/q.json?b=$data").parsedSafe<OsResult>()?.result?.all?.map { sub -> | ||||
|             subtitleCallback.invoke( | ||||
|                 SubtitleFile( | ||||
|                     SubtitleHelper.fromThreeLettersToLanguage(sub.lang ?: "") ?: sub.lang | ||||
|                     ?: "", | ||||
|                     sub.url ?: return@map | ||||
|                 ) | ||||
|             ) | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     private suspend fun invokeWatchsomuch( | ||||
|         imdbId: String? = null, | ||||
|         season: Int? = null, | ||||
|         episode: Int? = null, | ||||
|         subtitleCallback: (SubtitleFile) -> Unit, | ||||
|     ) { | ||||
|         val id = imdbId?.removePrefix("tt") | ||||
|         val epsId = app.post( | ||||
|             "${watchSomuchAPI}/Watch/ajMovieTorrents.aspx", data = mapOf( | ||||
|                 "index" to "0", | ||||
|                 "mid" to "$id", | ||||
|                 "wsk" to "f6ea6cde-e42b-4c26-98d3-b4fe48cdd4fb", | ||||
|                 "lid" to "", | ||||
|                 "liu" to "" | ||||
|             ), headers = mapOf("X-Requested-With" to "XMLHttpRequest") | ||||
|         ).parsedSafe<WatchsomuchResponses>()?.movie?.torrents?.let { eps -> | ||||
|             if (season == null) { | ||||
|                 eps.firstOrNull()?.id | ||||
|             } else { | ||||
|                 eps.find { it.episode == episode && it.season == season }?.id | ||||
|             } | ||||
|         } ?: return | ||||
| 
 | ||||
|         val (seasonSlug, episodeSlug) = getEpisodeSlug(season, episode) | ||||
| 
 | ||||
|         val subUrl = if (season == null) { | ||||
|             "${watchSomuchAPI}/Watch/ajMovieSubtitles.aspx?mid=$id&tid=$epsId&part=" | ||||
|         } else { | ||||
|             "${watchSomuchAPI}/Watch/ajMovieSubtitles.aspx?mid=$id&tid=$epsId&part=S${seasonSlug}E${episodeSlug}" | ||||
|         } | ||||
| 
 | ||||
|         app.get(subUrl).parsedSafe<WatchsomuchSubResponses>()?.subtitles?.map { sub -> | ||||
|             subtitleCallback.invoke( | ||||
|                 SubtitleFile( | ||||
|                     sub.label ?: "", fixUrl(sub.url ?: return@map null, watchSomuchAPI) | ||||
|                 ) | ||||
|             ) | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     private data class StreamsResponse(val streams: List<Stream>) | ||||
|     private data class Subtitle( | ||||
|         val url: String?, | ||||
|  | @ -396,43 +332,6 @@ open class StremioX : MainAPI() { | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     data class OsSubtitles( | ||||
|         @JsonProperty("url") val url: String? = null, | ||||
|         @JsonProperty("lang") val lang: String? = null, | ||||
|     ) | ||||
| 
 | ||||
|     data class OsAll( | ||||
|         @JsonProperty("all") val all: ArrayList<OsSubtitles>? = arrayListOf(), | ||||
|     ) | ||||
| 
 | ||||
|     data class OsResult( | ||||
|         @JsonProperty("result") val result: OsAll? = null, | ||||
|     ) | ||||
| 
 | ||||
|     data class WatchsomuchTorrents( | ||||
|         @JsonProperty("id") val id: Int? = null, | ||||
|         @JsonProperty("movieId") val movieId: Int? = null, | ||||
|         @JsonProperty("season") val season: Int? = null, | ||||
|         @JsonProperty("episode") val episode: Int? = null, | ||||
|     ) | ||||
| 
 | ||||
|     data class WatchsomuchMovies( | ||||
|         @JsonProperty("torrents") val torrents: ArrayList<WatchsomuchTorrents>? = arrayListOf(), | ||||
|     ) | ||||
| 
 | ||||
|     data class WatchsomuchResponses( | ||||
|         @JsonProperty("movie") val movie: WatchsomuchMovies? = null, | ||||
|     ) | ||||
| 
 | ||||
|     data class WatchsomuchSubtitles( | ||||
|         @JsonProperty("url") val url: String? = null, | ||||
|         @JsonProperty("label") val label: String? = null, | ||||
|     ) | ||||
| 
 | ||||
|     data class WatchsomuchSubResponses( | ||||
|         @JsonProperty("subtitles") val subtitles: ArrayList<WatchsomuchSubtitles>? = arrayListOf(), | ||||
|     ) | ||||
| 
 | ||||
|     data class LoadData( | ||||
|         val imdbId: String? = null, | ||||
|         val season: Int? = null, | ||||
|  |  | |||
							
								
								
									
										115
									
								
								StremioX/src/main/kotlin/com/hexated/SubsExtractors.kt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								StremioX/src/main/kotlin/com/hexated/SubsExtractors.kt
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,115 @@ | |||
| package com.hexated | ||||
| 
 | ||||
| import com.fasterxml.jackson.annotation.JsonProperty | ||||
| import com.lagradost.cloudstream3.SubtitleFile | ||||
| import com.lagradost.cloudstream3.app | ||||
| import com.lagradost.cloudstream3.base64Encode | ||||
| import com.lagradost.cloudstream3.utils.SubtitleHelper | ||||
| 
 | ||||
| const val openSubAPI = "https://opensubtitles.strem.io/stremio/v1" | ||||
| const val watchSomuchAPI = "https://watchsomuch.tv" | ||||
| 
 | ||||
| object SubsExtractors { | ||||
|     suspend fun invokeOpenSubs( | ||||
|         imdbId: String? = null, | ||||
|         season: Int? = null, | ||||
|         episode: Int? = null, | ||||
|         subtitleCallback: (SubtitleFile) -> Unit, | ||||
|     ) { | ||||
|         val id = if(season == null) { | ||||
|             imdbId | ||||
|         } else { | ||||
|             "$imdbId $season $episode" | ||||
|         } | ||||
|         val data = base64Encode("""{"id":1,"jsonrpc":"2.0","method":"subtitles.find","params":[null,{"query":{"itemHash":"$id"}}]}""".toByteArray()) | ||||
|         app.get("${openSubAPI}/q.json?b=$data").parsedSafe<OsResult>()?.result?.all?.map { sub -> | ||||
|             subtitleCallback.invoke( | ||||
|                 SubtitleFile( | ||||
|                     SubtitleHelper.fromThreeLettersToLanguage(sub.lang ?: "") ?: sub.lang | ||||
|                     ?: "", | ||||
|                     sub.url ?: return@map | ||||
|                 ) | ||||
|             ) | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     suspend fun invokeWatchsomuch( | ||||
|         imdbId: String? = null, | ||||
|         season: Int? = null, | ||||
|         episode: Int? = null, | ||||
|         subtitleCallback: (SubtitleFile) -> Unit, | ||||
|     ) { | ||||
|         val id = imdbId?.removePrefix("tt") | ||||
|         val epsId = app.post( | ||||
|             "${watchSomuchAPI}/Watch/ajMovieTorrents.aspx", data = mapOf( | ||||
|                 "index" to "0", | ||||
|                 "mid" to "$id", | ||||
|                 "wsk" to "f6ea6cde-e42b-4c26-98d3-b4fe48cdd4fb", | ||||
|                 "lid" to "", | ||||
|                 "liu" to "" | ||||
|             ), headers = mapOf("X-Requested-With" to "XMLHttpRequest") | ||||
|         ).parsedSafe<WatchsomuchResponses>()?.movie?.torrents?.let { eps -> | ||||
|             if (season == null) { | ||||
|                 eps.firstOrNull()?.id | ||||
|             } else { | ||||
|                 eps.find { it.episode == episode && it.season == season }?.id | ||||
|             } | ||||
|         } ?: return | ||||
| 
 | ||||
|         val (seasonSlug, episodeSlug) = getEpisodeSlug(season, episode) | ||||
| 
 | ||||
|         val subUrl = if (season == null) { | ||||
|             "${watchSomuchAPI}/Watch/ajMovieSubtitles.aspx?mid=$id&tid=$epsId&part=" | ||||
|         } else { | ||||
|             "${watchSomuchAPI}/Watch/ajMovieSubtitles.aspx?mid=$id&tid=$epsId&part=S${seasonSlug}E${episodeSlug}" | ||||
|         } | ||||
| 
 | ||||
|         app.get(subUrl).parsedSafe<WatchsomuchSubResponses>()?.subtitles?.map { sub -> | ||||
|             subtitleCallback.invoke( | ||||
|                 SubtitleFile( | ||||
|                     sub.label ?: "", fixUrl(sub.url ?: return@map null, watchSomuchAPI) | ||||
|                 ) | ||||
|             ) | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     data class OsSubtitles( | ||||
|         @JsonProperty("url") val url: String? = null, | ||||
|         @JsonProperty("lang") val lang: String? = null, | ||||
|     ) | ||||
| 
 | ||||
|     data class OsAll( | ||||
|         @JsonProperty("all") val all: ArrayList<OsSubtitles>? = arrayListOf(), | ||||
|     ) | ||||
| 
 | ||||
|     data class OsResult( | ||||
|         @JsonProperty("result") val result: OsAll? = null, | ||||
|     ) | ||||
| 
 | ||||
|     data class WatchsomuchTorrents( | ||||
|         @JsonProperty("id") val id: Int? = null, | ||||
|         @JsonProperty("movieId") val movieId: Int? = null, | ||||
|         @JsonProperty("season") val season: Int? = null, | ||||
|         @JsonProperty("episode") val episode: Int? = null, | ||||
|     ) | ||||
| 
 | ||||
|     data class WatchsomuchMovies( | ||||
|         @JsonProperty("torrents") val torrents: ArrayList<WatchsomuchTorrents>? = arrayListOf(), | ||||
|     ) | ||||
| 
 | ||||
|     data class WatchsomuchResponses( | ||||
|         @JsonProperty("movie") val movie: WatchsomuchMovies? = null, | ||||
|     ) | ||||
| 
 | ||||
|     data class WatchsomuchSubtitles( | ||||
|         @JsonProperty("url") val url: String? = null, | ||||
|         @JsonProperty("label") val label: String? = null, | ||||
|     ) | ||||
| 
 | ||||
|     data class WatchsomuchSubResponses( | ||||
|         @JsonProperty("subtitles") val subtitles: ArrayList<WatchsomuchSubtitles>? = arrayListOf(), | ||||
|     ) | ||||
| } | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue