forked from recloudstream/cloudstream
		
	small subtitle support
This commit is contained in:
		
							parent
							
								
									672dc8c8d1
								
							
						
					
					
						commit
						9fc732c68c
					
				
					 12 changed files with 266 additions and 124 deletions
				
			
		|  | @ -72,7 +72,7 @@ abstract class MainAPI { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // callback is fired once a link is found, will return true if method is executed successfully |     // callback is fired once a link is found, will return true if method is executed successfully | ||||||
|     open fun loadLinks(data: String, isCasting: Boolean, callback: (ExtractorLink) -> Unit): Boolean { |     open fun loadLinks(data: String, isCasting: Boolean, subtitleCallback : (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit): Boolean { | ||||||
|         return false |         return false | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -120,6 +120,8 @@ enum class TvType { | ||||||
|     ONA, |     ONA, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | data class SubtitleFile(val lang : String, val url : String) | ||||||
|  | 
 | ||||||
| interface SearchResponse { | interface SearchResponse { | ||||||
|     val name: String |     val name: String | ||||||
|     val url: String // PUBLIC URL FOR OPEN IN APP |     val url: String // PUBLIC URL FOR OPEN IN APP | ||||||
|  |  | ||||||
|  | @ -86,7 +86,8 @@ class DubbedAnimeProvider : MainAPI() { | ||||||
|                         title, href, getSlug(href), this.name, TvType.Movie, img, null |                         title, href, getSlug(href), this.name, TvType.Movie, img, null | ||||||
|                     ) |                     ) | ||||||
|                 } else { |                 } else { | ||||||
|                     AnimeSearchResponse(title, |                     AnimeSearchResponse( | ||||||
|  |                         title, | ||||||
|                         href, |                         href, | ||||||
|                         getSlug(href), |                         getSlug(href), | ||||||
|                         this.name, |                         this.name, | ||||||
|  | @ -96,8 +97,10 @@ class DubbedAnimeProvider : MainAPI() { | ||||||
|                         null, |                         null, | ||||||
|                         EnumSet.of(DubStatus.Dubbed), |                         EnumSet.of(DubStatus.Dubbed), | ||||||
|                         null, |                         null, | ||||||
|                         null) |                         null | ||||||
|                 }) |                     ) | ||||||
|  |                 } | ||||||
|  |             ) | ||||||
|         } |         } | ||||||
|         return returnValue |         return returnValue | ||||||
|     } |     } | ||||||
|  | @ -121,7 +124,8 @@ class DubbedAnimeProvider : MainAPI() { | ||||||
|                         title, href, getSlug(href), this.name, TvType.Movie, img, null |                         title, href, getSlug(href), this.name, TvType.Movie, img, null | ||||||
|                     ) |                     ) | ||||||
|                 } else { |                 } else { | ||||||
|                     AnimeSearchResponse(title, |                     AnimeSearchResponse( | ||||||
|  |                         title, | ||||||
|                         href, |                         href, | ||||||
|                         getSlug(href), |                         getSlug(href), | ||||||
|                         this.name, |                         this.name, | ||||||
|  | @ -131,14 +135,21 @@ class DubbedAnimeProvider : MainAPI() { | ||||||
|                         null, |                         null, | ||||||
|                         EnumSet.of(DubStatus.Dubbed), |                         EnumSet.of(DubStatus.Dubbed), | ||||||
|                         null, |                         null, | ||||||
|                         null) |                         null | ||||||
|                 }) |                     ) | ||||||
|  |                 } | ||||||
|  |             ) | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         return returnValue |         return returnValue | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     override fun loadLinks(data: String, isCasting: Boolean, callback: (ExtractorLink) -> Unit): Boolean { |     override fun loadLinks( | ||||||
|  |         data: String, | ||||||
|  |         isCasting: Boolean, | ||||||
|  |         subtitleCallback: (SubtitleFile) -> Unit, | ||||||
|  |         callback: (ExtractorLink) -> Unit | ||||||
|  |     ): Boolean { | ||||||
|         val serversHTML = (if (data.startsWith(mainUrl)) { // CLASSIC EPISODE |         val serversHTML = (if (data.startsWith(mainUrl)) { // CLASSIC EPISODE | ||||||
|             val slug = getSlug(data) |             val slug = getSlug(data) | ||||||
|             getAnimeEpisode(slug, false).serversHTML |             getAnimeEpisode(slug, false).serversHTML | ||||||
|  | @ -154,11 +165,15 @@ class DubbedAnimeProvider : MainAPI() { | ||||||
|                 val find = "src=\"(.*?)\".*?label=\"(.*?)\"".toRegex().find(txt) |                 val find = "src=\"(.*?)\".*?label=\"(.*?)\"".toRegex().find(txt) | ||||||
|                 if (find != null) { |                 if (find != null) { | ||||||
|                     val quality = find.groupValues[2] |                     val quality = find.groupValues[2] | ||||||
|                     callback.invoke(ExtractorLink(this.name, |                     callback.invoke( | ||||||
|  |                         ExtractorLink( | ||||||
|  |                             this.name, | ||||||
|                             this.name + " " + quality + if (quality.endsWith('p')) "" else 'p', |                             this.name + " " + quality + if (quality.endsWith('p')) "" else 'p', | ||||||
|                             fixUrl(find.groupValues[1]), |                             fixUrl(find.groupValues[1]), | ||||||
|                             this.mainUrl, |                             this.mainUrl, | ||||||
|                         getQualityFromName(quality))) |                             getQualityFromName(quality) | ||||||
|  |                         ) | ||||||
|  |                     ) | ||||||
|                 } |                 } | ||||||
|             } catch (e: Exception) { |             } catch (e: Exception) { | ||||||
|                 //IDK |                 //IDK | ||||||
|  | @ -172,7 +187,8 @@ class DubbedAnimeProvider : MainAPI() { | ||||||
|             val realSlug = slug.replace("movies/", "") |             val realSlug = slug.replace("movies/", "") | ||||||
|             val episode = getAnimeEpisode(realSlug, true) |             val episode = getAnimeEpisode(realSlug, true) | ||||||
|             val poster = episode.previewImg ?: episode.wideImg |             val poster = episode.previewImg ?: episode.wideImg | ||||||
|             return MovieLoadResponse(episode.title, |             return MovieLoadResponse( | ||||||
|  |                 episode.title, | ||||||
|                 realSlug, |                 realSlug, | ||||||
|                 this.name, |                 this.name, | ||||||
|                 TvType.Movie, |                 TvType.Movie, | ||||||
|  | @ -180,7 +196,8 @@ class DubbedAnimeProvider : MainAPI() { | ||||||
|                 if (poster == null) null else fixUrl(poster), |                 if (poster == null) null else fixUrl(poster), | ||||||
|                 episode.year?.toIntOrNull(), |                 episode.year?.toIntOrNull(), | ||||||
|                 episode.desc, |                 episode.desc, | ||||||
|                 null) |                 null | ||||||
|  |             ) | ||||||
|         } else { |         } else { | ||||||
|             val response = khttp.get("$mainUrl/$slug") |             val response = khttp.get("$mainUrl/$slug") | ||||||
|             val document = Jsoup.parse(response.text) |             val document = Jsoup.parse(response.text) | ||||||
|  |  | ||||||
|  | @ -167,12 +167,14 @@ class ShiroProvider : MainAPI() { | ||||||
|     override fun quickSearch(query: String): ArrayList<SearchResponse> { |     override fun quickSearch(query: String): ArrayList<SearchResponse> { | ||||||
|         val returnValue: ArrayList<SearchResponse> = ArrayList() |         val returnValue: ArrayList<SearchResponse> = ArrayList() | ||||||
| 
 | 
 | ||||||
|         val response = khttp.get("https://tapi.shiro.is/anime/auto-complete/${ |         val response = khttp.get( | ||||||
|  |             "https://tapi.shiro.is/anime/auto-complete/${ | ||||||
|                 URLEncoder.encode( |                 URLEncoder.encode( | ||||||
|                     query, |                     query, | ||||||
|                     "UTF-8" |                     "UTF-8" | ||||||
|                 ) |                 ) | ||||||
|         }?token=$token".replace("+", "%20")) |             }?token=$token".replace("+", "%20") | ||||||
|  |         ) | ||||||
|         if (response.text == "{\"status\":\"Found\",\"data\":[]}") return returnValue // OR ELSE WILL CAUSE WEIRD ERROR |         if (response.text == "{\"status\":\"Found\",\"data\":[]}") return returnValue // OR ELSE WILL CAUSE WEIRD ERROR | ||||||
| 
 | 
 | ||||||
|         val mapped = response.let { mapper.readValue<ShiroSearchResponse>(it.text) } |         val mapped = response.let { mapper.readValue<ShiroSearchResponse>(it.text) } | ||||||
|  | @ -185,12 +187,14 @@ class ShiroProvider : MainAPI() { | ||||||
|     override fun search(query: String): ArrayList<SearchResponse>? { |     override fun search(query: String): ArrayList<SearchResponse>? { | ||||||
|         if (!autoLoadToken()) return null |         if (!autoLoadToken()) return null | ||||||
|         val returnValue: ArrayList<SearchResponse> = ArrayList() |         val returnValue: ArrayList<SearchResponse> = ArrayList() | ||||||
|         val response = khttp.get("https://tapi.shiro.is/advanced?search=${ |         val response = khttp.get( | ||||||
|  |             "https://tapi.shiro.is/advanced?search=${ | ||||||
|                 URLEncoder.encode( |                 URLEncoder.encode( | ||||||
|                     query, |                     query, | ||||||
|                     "UTF-8" |                     "UTF-8" | ||||||
|                 ) |                 ) | ||||||
|         }&token=$token".replace("+", "%20")) |             }&token=$token".replace("+", "%20") | ||||||
|  |         ) | ||||||
|         if (response.text == "{\"status\":\"Found\",\"data\":[]}") return returnValue // OR ELSE WILL CAUSE WEIRD ERROR |         if (response.text == "{\"status\":\"Found\",\"data\":[]}") return returnValue // OR ELSE WILL CAUSE WEIRD ERROR | ||||||
| 
 | 
 | ||||||
|         val mapped = response.let { mapper.readValue<ShiroFullSearchResponse>(it.text) } |         val mapped = response.let { mapper.readValue<ShiroFullSearchResponse>(it.text) } | ||||||
|  | @ -235,7 +239,12 @@ class ShiroProvider : MainAPI() { | ||||||
|         ) |         ) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     override fun loadLinks(data: String, isCasting: Boolean, callback: (ExtractorLink) -> Unit): Boolean { |     override fun loadLinks( | ||||||
|  |         data: String, | ||||||
|  |         isCasting: Boolean, | ||||||
|  |         subtitleCallback: (SubtitleFile) -> Unit, | ||||||
|  |         callback: (ExtractorLink) -> Unit | ||||||
|  |     ): Boolean { | ||||||
|         return Vidstream().getUrl(data, isCasting) { |         return Vidstream().getUrl(data, isCasting) { | ||||||
|             callback.invoke(it) |             callback.invoke(it) | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  | @ -29,17 +29,26 @@ class HDMProvider : MainAPI() { | ||||||
|         return returnValue |         return returnValue | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     override fun loadLinks(data: String, isCasting: Boolean, callback: (ExtractorLink) -> Unit): Boolean { |     override fun loadLinks( | ||||||
|  |         data: String, | ||||||
|  |         isCasting: Boolean, | ||||||
|  |         subtitleCallback: (SubtitleFile) -> Unit, | ||||||
|  |         callback: (ExtractorLink) -> Unit | ||||||
|  |     ): Boolean { | ||||||
|         if (data == "") return false |         if (data == "") return false | ||||||
|         val slug = ".*/(.*?)\\.mp4".toRegex().find(data)?.groupValues?.get(1) ?: return false |         val slug = ".*/(.*?)\\.mp4".toRegex().find(data)?.groupValues?.get(1) ?: return false | ||||||
|         val response = khttp.get(data) |         val response = khttp.get(data) | ||||||
|         val key = "playlist\\.m3u8(.*?)\"".toRegex().find(response.text)?.groupValues?.get(1) ?: return false |         val key = "playlist\\.m3u8(.*?)\"".toRegex().find(response.text)?.groupValues?.get(1) ?: return false | ||||||
|         callback.invoke(ExtractorLink(this.name, |         callback.invoke( | ||||||
|  |             ExtractorLink( | ||||||
|  |                 this.name, | ||||||
|                 this.name, |                 this.name, | ||||||
|                 "https://hls.1o.to/vod/$slug/playlist.m3u8$key", |                 "https://hls.1o.to/vod/$slug/playlist.m3u8$key", | ||||||
|                 "", |                 "", | ||||||
|                 Qualities.HD.value, |                 Qualities.HD.value, | ||||||
|             true)) |                 true | ||||||
|  |             ) | ||||||
|  |         ) | ||||||
|         return true |         return true | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -53,7 +62,9 @@ class HDMProvider : MainAPI() { | ||||||
|             ?.toIntOrNull() |             ?.toIntOrNull() | ||||||
|         val data = "src/player/\\?v=(.*?)\"".toRegex().find(response.text)?.groupValues?.get(1) ?: return null |         val data = "src/player/\\?v=(.*?)\"".toRegex().find(response.text)?.groupValues?.get(1) ?: return null | ||||||
| 
 | 
 | ||||||
|         return MovieLoadResponse(title, slug, this.name, TvType.Movie, |         return MovieLoadResponse( | ||||||
|             "$mainUrl/src/player/?v=$data", poster, year, descript, null) |             title, slug, this.name, TvType.Movie, | ||||||
|  |             "$mainUrl/src/player/?v=$data", poster, year, descript, null | ||||||
|  |         ) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -5,7 +5,6 @@ import com.fasterxml.jackson.module.kotlin.readValue | ||||||
| import com.lagradost.cloudstream3.* | import com.lagradost.cloudstream3.* | ||||||
| import com.lagradost.cloudstream3.APIHolder.unixTime | import com.lagradost.cloudstream3.APIHolder.unixTime | ||||||
| import com.lagradost.cloudstream3.utils.ExtractorLink | import com.lagradost.cloudstream3.utils.ExtractorLink | ||||||
| import com.lagradost.cloudstream3.utils.Qualities |  | ||||||
| import com.lagradost.cloudstream3.utils.extractors.M3u8Manifest | import com.lagradost.cloudstream3.utils.extractors.M3u8Manifest | ||||||
| import com.lagradost.cloudstream3.utils.getQualityFromName | import com.lagradost.cloudstream3.utils.getQualityFromName | ||||||
| import org.jsoup.Jsoup | import org.jsoup.Jsoup | ||||||
|  | @ -43,7 +42,7 @@ class LookMovieProvider : MainAPI() { | ||||||
| 
 | 
 | ||||||
|     data class LookMovieTokenSubtitle( |     data class LookMovieTokenSubtitle( | ||||||
|         @JsonProperty("language") val language: String, |         @JsonProperty("language") val language: String, | ||||||
|         //@JsonProperty("source") val source: String, |         @JsonProperty("source") val source: String?, | ||||||
|         //@JsonProperty("source_id") val source_id: String, |         //@JsonProperty("source_id") val source_id: String, | ||||||
|         //@JsonProperty("kind") val kind: String, |         //@JsonProperty("kind") val kind: String, | ||||||
|         //@JsonProperty("id") val id: String, |         //@JsonProperty("id") val id: String, | ||||||
|  | @ -77,26 +76,34 @@ class LookMovieProvider : MainAPI() { | ||||||
|         if (!movies.isNullOrEmpty()) { |         if (!movies.isNullOrEmpty()) { | ||||||
|             for (m in movies) { |             for (m in movies) { | ||||||
|                 val url = "$mainUrl/movies/view/${m.slug}" |                 val url = "$mainUrl/movies/view/${m.slug}" | ||||||
|                 returnValue.add(MovieSearchResponse(m.title, |                 returnValue.add( | ||||||
|  |                     MovieSearchResponse( | ||||||
|  |                         m.title, | ||||||
|                         url, |                         url, | ||||||
|                         url,//m.slug, |                         url,//m.slug, | ||||||
|                         this.name, |                         this.name, | ||||||
|                         TvType.Movie, |                         TvType.Movie, | ||||||
|                         m.poster ?: m.backdrop, |                         m.poster ?: m.backdrop, | ||||||
|                     m.year?.toIntOrNull())) |                         m.year?.toIntOrNull() | ||||||
|  |                     ) | ||||||
|  |                 ) | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if (!shows.isNullOrEmpty()) { |         if (!shows.isNullOrEmpty()) { | ||||||
|             for (s in shows) { |             for (s in shows) { | ||||||
|                 val url = "$mainUrl/shows/view/${s.slug}" |                 val url = "$mainUrl/shows/view/${s.slug}" | ||||||
|                 returnValue.add(MovieSearchResponse(s.title, |                 returnValue.add( | ||||||
|  |                     MovieSearchResponse( | ||||||
|  |                         s.title, | ||||||
|                         url, |                         url, | ||||||
|                         url,//s.slug, |                         url,//s.slug, | ||||||
|                         this.name, |                         this.name, | ||||||
|                         TvType.TvSeries, |                         TvType.TvSeries, | ||||||
|                         s.poster ?: s.backdrop, |                         s.poster ?: s.backdrop, | ||||||
|                     s.year?.toIntOrNull())) |                         s.year?.toIntOrNull() | ||||||
|  |                     ) | ||||||
|  |                 ) | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  | @ -119,7 +126,8 @@ class LookMovieProvider : MainAPI() { | ||||||
|                 val poster = posterHolder.selectFirst("> img")?.attr("data-src") |                 val poster = posterHolder.selectFirst("> img")?.attr("data-src") | ||||||
|                 val year = posterHolder.selectFirst("> p.year")?.text()?.toIntOrNull() |                 val year = posterHolder.selectFirst("> p.year")?.text()?.toIntOrNull() | ||||||
| 
 | 
 | ||||||
|                 returnValue.add(if (isMovie) { |                 returnValue.add( | ||||||
|  |                     if (isMovie) { | ||||||
|                         MovieSearchResponse( |                         MovieSearchResponse( | ||||||
|                             name, href, href, this.name, TvType.Movie, poster, year |                             name, href, href, this.name, TvType.Movie, poster, year | ||||||
|                         ) |                         ) | ||||||
|  | @ -138,15 +146,52 @@ class LookMovieProvider : MainAPI() { | ||||||
|         return movieList |         return movieList | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     override fun loadLinks(data: String, isCasting: Boolean, callback: (ExtractorLink) -> Unit): Boolean { |     data class LookMovieLinkLoad(val url: String, val extraUrl: String, val isMovie: Boolean) | ||||||
|         val response = khttp.get(data.replace("\$unixtime", unixTime.toString())) | 
 | ||||||
|  |     private fun addSubtitles(subs: List<LookMovieTokenSubtitle>?, subtitleCallback: (SubtitleFile) -> Unit) { | ||||||
|  |         if (subs == null) return | ||||||
|  |         subs.forEach { | ||||||
|  |             if (it.source != "opensubtitle") | ||||||
|  |                 subtitleCallback.invoke(SubtitleFile(it.language, fixUrl(it.file))) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private fun loadCurrentLinks(url: String, callback: (ExtractorLink) -> Unit) { | ||||||
|  |         val response = khttp.get(url.replace("\$unixtime", unixTime.toString())) | ||||||
|         M3u8Manifest.extractLinks(response.text).forEach { |         M3u8Manifest.extractLinks(response.text).forEach { | ||||||
|             callback.invoke(ExtractorLink(this.name, |             callback.invoke( | ||||||
|  |                 ExtractorLink( | ||||||
|  |                     this.name, | ||||||
|                     "${this.name} - ${it.second}", |                     "${this.name} - ${it.second}", | ||||||
|                     fixUrl(it.first), |                     fixUrl(it.first), | ||||||
|                     "", |                     "", | ||||||
|                     getQualityFromName(it.second), |                     getQualityFromName(it.second), | ||||||
|                 true)) |                     true | ||||||
|  |                 ) | ||||||
|  |             ) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     override fun loadLinks( | ||||||
|  |         data: String, | ||||||
|  |         isCasting: Boolean, | ||||||
|  |         subtitleCallback: (SubtitleFile) -> Unit, | ||||||
|  |         callback: (ExtractorLink) -> Unit | ||||||
|  |     ): Boolean { | ||||||
|  |         val localData: LookMovieLinkLoad = mapper.readValue(data) | ||||||
|  | 
 | ||||||
|  |         if (localData.isMovie) { | ||||||
|  |             val tokenResponse = khttp.get(localData.url) | ||||||
|  |             val root = mapper.readValue<LookMovieTokenRoot>(tokenResponse.text) | ||||||
|  |             val accessToken = root.data?.accessToken ?: return false | ||||||
|  |             addSubtitles(root.data.subtitles, subtitleCallback) | ||||||
|  |             loadCurrentLinks(localData.extraUrl.replace("\$accessToken", accessToken), callback) | ||||||
|  |             return true | ||||||
|  |         } else { | ||||||
|  |             loadCurrentLinks(localData.url, callback) | ||||||
|  |             val subResponse = khttp.get(localData.extraUrl) | ||||||
|  |             val subs = mapper.readValue<List<LookMovieTokenSubtitle>>(subResponse.text) | ||||||
|  |             addSubtitles(subs, subtitleCallback) | ||||||
|         } |         } | ||||||
|         return true |         return true | ||||||
|     } |     } | ||||||
|  | @ -159,7 +204,7 @@ class LookMovieProvider : MainAPI() { | ||||||
|         val watchHeader = document.selectFirst("div.watch-heading") |         val watchHeader = document.selectFirst("div.watch-heading") | ||||||
|         val nameHeader = watchHeader.selectFirst("> h1.bd-hd") |         val nameHeader = watchHeader.selectFirst("> h1.bd-hd") | ||||||
|         val year = nameHeader.selectFirst("> span")?.text()?.toIntOrNull() |         val year = nameHeader.selectFirst("> span")?.text()?.toIntOrNull() | ||||||
|         val name = nameHeader.ownText() |         val title = nameHeader.ownText() | ||||||
|         val rating = parseRating(watchHeader.selectFirst("> div.movie-rate > div.rate > p > span").text()) |         val rating = parseRating(watchHeader.selectFirst("> div.movie-rate > div.rate > p > span").text()) | ||||||
|         val imgElement = document.selectFirst("div.movie-img > p.movie__poster") |         val imgElement = document.selectFirst("div.movie-img > p.movie__poster") | ||||||
|         val img = imgElement?.attr("style") |         val img = imgElement?.attr("style") | ||||||
|  | @ -173,22 +218,31 @@ class LookMovieProvider : MainAPI() { | ||||||
|         val realUrl = |         val realUrl = | ||||||
|             "$mainUrl/api/v1/security/${if (isMovie) "movie" else "show"}-access?${if (isMovie) "id_movie=$id" else "slug=$realSlug"}&token=1&sk=&step=1" |             "$mainUrl/api/v1/security/${if (isMovie) "movie" else "show"}-access?${if (isMovie) "id_movie=$id" else "slug=$realSlug"}&token=1&sk=&step=1" | ||||||
| 
 | 
 | ||||||
|         val tokenResponse = khttp.get(realUrl) |  | ||||||
|         val root = mapper.readValue<LookMovieTokenRoot>(tokenResponse.text) |  | ||||||
|         val accessToken = root.data?.accessToken ?: return null |  | ||||||
| 
 |  | ||||||
|         if (isMovie) { |         if (isMovie) { | ||||||
|             return MovieLoadResponse(name, |             val localData = mapper.writeValueAsString( | ||||||
|  |                 LookMovieLinkLoad( | ||||||
|  |                     realUrl, | ||||||
|  |                     "$mainUrl/manifests/movies/json/$id/\$unixtime/\$accessToken/master.m3u8", | ||||||
|  |                     true | ||||||
|  |                 ) | ||||||
|  |             ) | ||||||
|  |             return MovieLoadResponse( | ||||||
|  |                 title, | ||||||
|                 slug, |                 slug, | ||||||
|                 this.name, |                 this.name, | ||||||
|                 TvType.Movie, |                 TvType.Movie, | ||||||
|                 "$mainUrl/manifests/movies/json/$id/\$unixtime/$accessToken/master.m3u8", |                 localData, | ||||||
|                 poster, |                 poster, | ||||||
|                 year, |                 year, | ||||||
|                 descript, |                 descript, | ||||||
|                 null, |                 null, | ||||||
|                 rating) |                 rating | ||||||
|  |             ) | ||||||
|         } else { |         } else { | ||||||
|  |             val tokenResponse = khttp.get(realUrl) | ||||||
|  |             val root = mapper.readValue<LookMovieTokenRoot>(tokenResponse.text) | ||||||
|  |             val accessToken = root.data?.accessToken ?: return null | ||||||
|  | 
 | ||||||
|             val window = |             val window = | ||||||
|                 "window\\[\\'show_storage\\'\\] =((.|\\n)*?\\<)".toRegex().find(response.text)?.groupValues?.get(1) |                 "window\\[\\'show_storage\\'\\] =((.|\\n)*?\\<)".toRegex().find(response.text)?.groupValues?.get(1) | ||||||
|                     ?: return null |                     ?: return null | ||||||
|  | @ -208,13 +262,24 @@ class LookMovieProvider : MainAPI() { | ||||||
|             val realJson = "[" + json.substring(0, json.lastIndexOf(',')) + "]" |             val realJson = "[" + json.substring(0, json.lastIndexOf(',')) + "]" | ||||||
| 
 | 
 | ||||||
|             val episodes = mapper.readValue<List<LookMovieEpisode>>(realJson).map { |             val episodes = mapper.readValue<List<LookMovieEpisode>>(realJson).map { | ||||||
|                 TvSeriesEpisode(it.title, |                 val localData = mapper.writeValueAsString( | ||||||
|  |                     LookMovieLinkLoad( | ||||||
|  |                         "$mainUrl/manifests/shows/json/$accessToken/\$unixtime/${it.idEpisode}/master.m3u8", | ||||||
|  |                         "https://lookmovie.io/api/v1/shows/episode-subtitles/?id_episode=${it.idEpisode}", | ||||||
|  |                         false | ||||||
|  |                     ) | ||||||
|  |                 ) | ||||||
|  | 
 | ||||||
|  |                 TvSeriesEpisode( | ||||||
|  |                     it.title, | ||||||
|                     it.season.toIntOrNull(), |                     it.season.toIntOrNull(), | ||||||
|                     it.episode.toIntOrNull(), |                     it.episode.toIntOrNull(), | ||||||
|                     "$mainUrl/manifests/shows/json/$accessToken/\$unixtime/${it.idEpisode}/master.m3u8") |                     localData | ||||||
|  |                 ) | ||||||
|             }.toList() |             }.toList() | ||||||
| 
 | 
 | ||||||
|             return TvSeriesLoadResponse(name, |             return TvSeriesLoadResponse( | ||||||
|  |                 title, | ||||||
|                 slug, |                 slug, | ||||||
|                 this.name, |                 this.name, | ||||||
|                 TvType.TvSeries, |                 TvType.TvSeries, | ||||||
|  | @ -224,7 +289,8 @@ class LookMovieProvider : MainAPI() { | ||||||
|                 descript, |                 descript, | ||||||
|                 null, |                 null, | ||||||
|                 null, |                 null, | ||||||
|                 rating) |                 rating | ||||||
|  |             ) | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -44,22 +44,30 @@ class MeloMovieProvider : MainAPI() { | ||||||
|             val currentUrl = "$mainUrl/movie/${i.id}" |             val currentUrl = "$mainUrl/movie/${i.id}" | ||||||
|             val currentPoster = "$mainUrl/assets/images/poster/${i.imdbId}.jpg" |             val currentPoster = "$mainUrl/assets/images/poster/${i.imdbId}.jpg" | ||||||
|             if (i.type == 2) { // TV-SERIES |             if (i.type == 2) { // TV-SERIES | ||||||
|                 returnValue.add(TvSeriesSearchResponse(i.title, |                 returnValue.add( | ||||||
|  |                     TvSeriesSearchResponse( | ||||||
|  |                         i.title, | ||||||
|                         currentUrl, |                         currentUrl, | ||||||
|                         currentUrl, |                         currentUrl, | ||||||
|                         this.name, |                         this.name, | ||||||
|                         TvType.TvSeries, |                         TvType.TvSeries, | ||||||
|                         currentPoster, |                         currentPoster, | ||||||
|                         i.year, |                         i.year, | ||||||
|                     null)) |                         null | ||||||
|  |                     ) | ||||||
|  |                 ) | ||||||
|             } else if (i.type == 1) { // MOVIE |             } else if (i.type == 1) { // MOVIE | ||||||
|                 returnValue.add(MovieSearchResponse(i.title, |                 returnValue.add( | ||||||
|  |                     MovieSearchResponse( | ||||||
|  |                         i.title, | ||||||
|                         currentUrl, |                         currentUrl, | ||||||
|                         currentUrl, |                         currentUrl, | ||||||
|                         this.name, |                         this.name, | ||||||
|                         TvType.Movie, |                         TvType.Movie, | ||||||
|                         currentUrl, |                         currentUrl, | ||||||
|                     i.year)) |                         i.year | ||||||
|  |                     ) | ||||||
|  |                 ) | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         return returnValue |         return returnValue | ||||||
|  | @ -67,7 +75,7 @@ class MeloMovieProvider : MainAPI() { | ||||||
| 
 | 
 | ||||||
|     // http not https, the links are not https! |     // http not https, the links are not https! | ||||||
|     private fun fixUrl(url: String): String { |     private fun fixUrl(url: String): String { | ||||||
|         if(url.isEmpty()) return "" |         if (url.isEmpty()) return "" | ||||||
| 
 | 
 | ||||||
|         if (url.startsWith("//")) { |         if (url.startsWith("//")) { | ||||||
|             return "http:$url" |             return "http:$url" | ||||||
|  | @ -93,7 +101,12 @@ class MeloMovieProvider : MainAPI() { | ||||||
|         return mapper.writeValueAsString(parsed) |         return mapper.writeValueAsString(parsed) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     override fun loadLinks(data: String, isCasting: Boolean, callback: (ExtractorLink) -> Unit): Boolean { |     override fun loadLinks( | ||||||
|  |         data: String, | ||||||
|  |         isCasting: Boolean, | ||||||
|  |         subtitleCallback: (SubtitleFile) -> Unit, | ||||||
|  |         callback: (ExtractorLink) -> Unit | ||||||
|  |     ): Boolean { | ||||||
|         val links = mapper.readValue<List<MeloMovieLink>>(data) |         val links = mapper.readValue<List<MeloMovieLink>>(data) | ||||||
|         for (link in links) { |         for (link in links) { | ||||||
|             callback.invoke(ExtractorLink(this.name, link.name, link.link, "", getQualityFromName(link.name), false)) |             callback.invoke(ExtractorLink(this.name, link.name, link.link, "", getQualityFromName(link.name), false)) | ||||||
|  | @ -120,7 +133,8 @@ class MeloMovieProvider : MainAPI() { | ||||||
| 
 | 
 | ||||||
|         if (type == 1) { // MOVIE |         if (type == 1) { // MOVIE | ||||||
|             val serialize = document.selectFirst("table.accordion__list") |             val serialize = document.selectFirst("table.accordion__list") | ||||||
|             return MovieLoadResponse(title, |             return MovieLoadResponse( | ||||||
|  |                 title, | ||||||
|                 slug, |                 slug, | ||||||
|                 this.name, |                 this.name, | ||||||
|                 TvType.Movie, |                 TvType.Movie, | ||||||
|  | @ -128,7 +142,8 @@ class MeloMovieProvider : MainAPI() { | ||||||
|                 poster, |                 poster, | ||||||
|                 year, |                 year, | ||||||
|                 plot, |                 plot, | ||||||
|                 imdbUrl) |                 imdbUrl | ||||||
|  |             ) | ||||||
|         } else if (type == 2) { |         } else if (type == 2) { | ||||||
|             val episodes = ArrayList<TvSeriesEpisode>() |             val episodes = ArrayList<TvSeriesEpisode>() | ||||||
|             val seasons = document.select("div.accordion__card") |             val seasons = document.select("div.accordion__card") | ||||||
|  | @ -145,7 +160,8 @@ class MeloMovieProvider : MainAPI() { | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|             episodes.reverse() |             episodes.reverse() | ||||||
|             return TvSeriesLoadResponse(title, |             return TvSeriesLoadResponse( | ||||||
|  |                 title, | ||||||
|                 slug, |                 slug, | ||||||
|                 this.name, |                 this.name, | ||||||
|                 TvType.TvSeries, |                 TvType.TvSeries, | ||||||
|  | @ -154,7 +170,8 @@ class MeloMovieProvider : MainAPI() { | ||||||
|                 year, |                 year, | ||||||
|                 plot, |                 plot, | ||||||
|                 null, |                 null, | ||||||
|                 imdbUrl) |                 imdbUrl | ||||||
|  |             ) | ||||||
|         } |         } | ||||||
|         return null |         return null | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -80,7 +80,12 @@ class TrailersToProvider : MainAPI() { | ||||||
|         return false |         return false | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     override fun loadLinks(data: String, isCasting: Boolean, callback: (ExtractorLink) -> Unit): Boolean { |     override fun loadLinks( | ||||||
|  |         data: String, | ||||||
|  |         isCasting: Boolean, | ||||||
|  |         subtitleCallback: (SubtitleFile) -> Unit, | ||||||
|  |         callback: (ExtractorLink) -> Unit | ||||||
|  |     ): Boolean { | ||||||
|         if (isCasting) return false |         if (isCasting) return false | ||||||
|         val isMovie = data.contains("/web-sources/") |         val isMovie = data.contains("/web-sources/") | ||||||
|         if (isMovie) { |         if (isMovie) { | ||||||
|  | @ -142,7 +147,8 @@ class TrailersToProvider : MainAPI() { | ||||||
|                 val epDescript = main.selectFirst("> p")?.text() |                 val epDescript = main.selectFirst("> p")?.text() | ||||||
|                 TvSeriesEpisode(epName, season, episode, href, epPoster, date, epRating, epDescript) |                 TvSeriesEpisode(epName, season, episode, href, epPoster, date, epRating, epDescript) | ||||||
|             } |             } | ||||||
|             return TvSeriesLoadResponse(title, |             return TvSeriesLoadResponse( | ||||||
|  |                 title, | ||||||
|                 slug, |                 slug, | ||||||
|                 this.name, |                 this.name, | ||||||
|                 TvType.TvSeries, |                 TvType.TvSeries, | ||||||
|  | @ -155,10 +161,12 @@ class TrailersToProvider : MainAPI() { | ||||||
|                 rating, |                 rating, | ||||||
|                 tags, |                 tags, | ||||||
|                 duration, |                 duration, | ||||||
|                 trailer) |                 trailer | ||||||
|  |             ) | ||||||
|         } else { |         } else { | ||||||
|             val data = fixUrl(document.selectFirst("content").attr("data-url") ?: return null) |             val data = fixUrl(document.selectFirst("content").attr("data-url") ?: return null) | ||||||
|             return MovieLoadResponse(title, |             return MovieLoadResponse( | ||||||
|  |                 title, | ||||||
|                 slug, |                 slug, | ||||||
|                 this.name, |                 this.name, | ||||||
|                 TvType.Movie, |                 TvType.Movie, | ||||||
|  | @ -170,7 +178,8 @@ class TrailersToProvider : MainAPI() { | ||||||
|                 rating, |                 rating, | ||||||
|                 tags, |                 tags, | ||||||
|                 duration, |                 duration, | ||||||
|                 trailer) |                 trailer | ||||||
|  |             ) | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -128,7 +128,8 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi | ||||||
|                                 epData, |                                 epData, | ||||||
|                                 holder, |                                 holder, | ||||||
|                                 index, |                                 index, | ||||||
|                                 remoteMediaClient?.mediaInfo?.customData) |                                 remoteMediaClient?.mediaInfo?.customData | ||||||
|  |                             ) | ||||||
| 
 | 
 | ||||||
|                             val startAt = remoteMediaClient?.approximateStreamPosition ?: 0 |                             val startAt = remoteMediaClient?.approximateStreamPosition ?: 0 | ||||||
| 
 | 
 | ||||||
|  | @ -139,12 +140,17 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi | ||||||
|                                 val nextId = remoteMediaClient.mediaQueue.itemIds?.get(currentIdIndex?.plus(1) ?: 0) |                                 val nextId = remoteMediaClient.mediaQueue.itemIds?.get(currentIdIndex?.plus(1) ?: 0) | ||||||
| 
 | 
 | ||||||
|                                 if (currentIdIndex == null && nextId != null) { |                                 if (currentIdIndex == null && nextId != null) { | ||||||
|                                     awaitLinks(remoteMediaClient?.queueInsertAndPlayItem(MediaQueueItem.Builder( |                                     awaitLinks( | ||||||
|                                         mediaItem) |                                         remoteMediaClient?.queueInsertAndPlayItem( | ||||||
|  |                                             MediaQueueItem.Builder( | ||||||
|  |                                                 mediaItem | ||||||
|  |                                             ) | ||||||
|                                                 .build(), |                                                 .build(), | ||||||
|                                             nextId, |                                             nextId, | ||||||
|                                             startAt, |                                             startAt, | ||||||
|                                         JSONObject())) { |                                             JSONObject() | ||||||
|  |                                         ) | ||||||
|  |                                     ) { | ||||||
|                                         loadMirror(index + 1) |                                         loadMirror(index + 1) | ||||||
|                                     } |                                     } | ||||||
|                                 } else { |                                 } else { | ||||||
|  | @ -204,7 +210,7 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi | ||||||
|                         val links = ArrayList<ExtractorLink>() |                         val links = ArrayList<ExtractorLink>() | ||||||
| 
 | 
 | ||||||
|                         val res = safeApiCall { |                         val res = safeApiCall { | ||||||
|                             getApiFromName(meta.apiName).loadLinks(epData.data, true) { |                             getApiFromName(meta.apiName).loadLinks(epData.data, true, { subtitleFile ->  }) { | ||||||
|                                 for (i in links) { |                                 for (i in links) { | ||||||
|                                     if (i.url == it.url) return@loadLinks |                                     if (i.url == it.url) return@loadLinks | ||||||
|                                 } |                                 } | ||||||
|  | @ -225,7 +231,8 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi | ||||||
|                                     epData, |                                     epData, | ||||||
|                                     jsonCopy, |                                     jsonCopy, | ||||||
|                                     0, |                                     0, | ||||||
|                                     done) |                                     done | ||||||
|  |                                 ) | ||||||
| 
 | 
 | ||||||
|                                 /*fun loadIndex(index: Int) { |                                 /*fun loadIndex(index: Int) { | ||||||
|                                     println("LOAD INDEX::::: $index") |                                     println("LOAD INDEX::::: $index") | ||||||
|  | @ -240,8 +247,12 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi | ||||||
|                                     } |                                     } | ||||||
|                                 }*/ |                                 }*/ | ||||||
| 
 | 
 | ||||||
|                                 awaitLinks(remoteMediaClient?.queueAppendItem(MediaQueueItem.Builder(mediaInfo).build(), |                                 awaitLinks( | ||||||
|                                     JSONObject())) { |                                     remoteMediaClient?.queueAppendItem( | ||||||
|  |                                         MediaQueueItem.Builder(mediaInfo).build(), | ||||||
|  |                                         JSONObject() | ||||||
|  |                                     ) | ||||||
|  |                                 ) { | ||||||
|                                     println("FAILED TO LOAD NEXT ITEM") |                                     println("FAILED TO LOAD NEXT ITEM") | ||||||
|                                     //  loadIndex(1) |                                     //  loadIndex(1) | ||||||
|                                 } |                                 } | ||||||
|  |  | ||||||
|  | @ -147,11 +147,11 @@ class EpisodeAdapter( | ||||||
|                         clickCallback.invoke(EpisodeClickEvent(ACTION_CHROME_CAST_EPISODE, card)) |                         clickCallback.invoke(EpisodeClickEvent(ACTION_CHROME_CAST_EPISODE, card)) | ||||||
|                     } else { |                     } else { | ||||||
|                         // clickCallback.invoke(EpisodeClickEvent(ACTION_PLAY_EPISODE_IN_PLAYER, card)) |                         // clickCallback.invoke(EpisodeClickEvent(ACTION_PLAY_EPISODE_IN_PLAYER, card)) | ||||||
|                         clickCallback.invoke(EpisodeClickEvent(ACTION_DOWNLOAD_EPISODE, card)) |                         clickCallback.invoke(EpisodeClickEvent(ACTION_PLAY_EPISODE_IN_PLAYER, card)) | ||||||
|                     } |                     } | ||||||
|                 } else { |                 } else { | ||||||
|                     // clickCallback.invoke(EpisodeClickEvent(ACTION_PLAY_EPISODE_IN_PLAYER, card)) |                     // clickCallback.invoke(EpisodeClickEvent(ACTION_PLAY_EPISODE_IN_PLAYER, card)) | ||||||
|                     clickCallback.invoke(EpisodeClickEvent(ACTION_DOWNLOAD_EPISODE, card)) |                     clickCallback.invoke(EpisodeClickEvent(ACTION_PLAY_EPISODE_IN_PLAYER, card)) | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  | @ -206,7 +206,7 @@ class ResultViewModel : ViewModel() { | ||||||
|             } |             } | ||||||
|             val links = ArrayList<ExtractorLink>() |             val links = ArrayList<ExtractorLink>() | ||||||
|             val localData = safeApiCall { |             val localData = safeApiCall { | ||||||
|                 getApiFromName(_apiName.value).loadLinks(data, isCasting) { |                 getApiFromName(_apiName.value).loadLinks(data, isCasting,  { subtitleFile ->  }) { | ||||||
|                     for (i in links) { |                     for (i in links) { | ||||||
|                         if (i.url == it.url) return@loadLinks |                         if (i.url == it.url) return@loadLinks | ||||||
|                     } |                     } | ||||||
|  |  | ||||||
|  | @ -148,7 +148,7 @@ class SearchFragment : Fragment() { | ||||||
|         allApi.providersActive = requireActivity().getApiSettings() |         allApi.providersActive = requireActivity().getApiSettings() | ||||||
| 
 | 
 | ||||||
|         //searchViewModel.search("iron man") |         //searchViewModel.search("iron man") | ||||||
|         (activity as AppCompatActivity).loadResult("https://shiro.is/overlord-dubbed", "overlord-dubbed", "Shiro") |         //(activity as AppCompatActivity).loadResult("https://shiro.is/overlord-dubbed", "overlord-dubbed", "Shiro") | ||||||
| /* | /* | ||||||
|         (requireActivity() as AppCompatActivity).supportFragmentManager.beginTransaction() |         (requireActivity() as AppCompatActivity).supportFragmentManager.beginTransaction() | ||||||
|             .setCustomAnimations(R.anim.enter_anim, |             .setCustomAnimations(R.anim.enter_anim, | ||||||
|  |  | ||||||
|  | @ -7,7 +7,7 @@ object M3u8Manifest { | ||||||
|         val data: ArrayList<Pair<String, String>> = ArrayList() |         val data: ArrayList<Pair<String, String>> = ArrayList() | ||||||
|         "\"(.*?)\":\"(.*?)\"".toRegex().findAll(m3u8Data).forEach { |         "\"(.*?)\":\"(.*?)\"".toRegex().findAll(m3u8Data).forEach { | ||||||
|             var quality = it.groupValues[1].replace("auto", "Auto") |             var quality = it.groupValues[1].replace("auto", "Auto") | ||||||
|             if (quality != "Auto") quality += "p" |             if (quality != "Auto" && !quality.endsWith('p')) quality += "p" | ||||||
|             val url = it.groupValues[2] |             val url = it.groupValues[2] | ||||||
|             data.add(Pair(url, quality)) |             data.add(Pair(url, quality)) | ||||||
|         } |         } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue