mirror of
				https://github.com/recloudstream/cloudstream.git
				synced 2024-08-15 01:53:11 +00:00 
			
		
		
		
	TrailersToProvider
This commit is contained in:
		
							parent
							
								
									cea1839b5c
								
							
						
					
					
						commit
						4cfb7c38c0
					
				
					 6 changed files with 255 additions and 46 deletions
				
			
		|  | @ -10,6 +10,7 @@ import com.lagradost.cloudstream3.animeproviders.ShiroProvider | |||
| import com.lagradost.cloudstream3.movieproviders.HDMProvider | ||||
| import com.lagradost.cloudstream3.movieproviders.LookMovieProvider | ||||
| import com.lagradost.cloudstream3.movieproviders.MeloMovieProvider | ||||
| import com.lagradost.cloudstream3.movieproviders.TrailersToProvider | ||||
| import com.lagradost.cloudstream3.utils.ExtractorLink | ||||
| import java.util.* | ||||
| import kotlin.collections.ArrayList | ||||
|  | @ -33,6 +34,7 @@ object APIHolder { | |||
|         DubbedAnimeProvider(), | ||||
|         HDMProvider(), | ||||
|         LookMovieProvider(), | ||||
|         TrailersToProvider(), | ||||
|     ) | ||||
| 
 | ||||
|     fun getApiFromName(apiName: String?): MainAPI { | ||||
|  | @ -61,7 +63,7 @@ abstract class MainAPI { | |||
|         return null | ||||
|     } | ||||
| 
 | ||||
|     open fun quickSearch(query: String) : ArrayList<SearchResponse>? { | ||||
|     open fun quickSearch(query: String): ArrayList<SearchResponse>? { | ||||
|         return null | ||||
|     } | ||||
| 
 | ||||
|  | @ -75,23 +77,22 @@ abstract class MainAPI { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| fun parseRating(ratingString : String?) : Int? { | ||||
|     if(ratingString == null) return null | ||||
| fun parseRating(ratingString: String?): Int? { | ||||
|     if (ratingString == null) return null | ||||
|     val floatRating = ratingString.toFloatOrNull() ?: return null | ||||
|     return (floatRating * 10).toInt() | ||||
| } | ||||
| 
 | ||||
| fun MainAPI.fixUrl(url: String): String { | ||||
|     if(url.startsWith("http")) { | ||||
|     if (url.startsWith("http")) { | ||||
|         return url | ||||
|     } | ||||
| 
 | ||||
|     val startsWithNoHttp = url.startsWith("//") | ||||
|     if(startsWithNoHttp) { | ||||
|     if (startsWithNoHttp) { | ||||
|         return "https:$url" | ||||
|     } | ||||
|     else { | ||||
|         if(url.startsWith('/')) { | ||||
|     } else { | ||||
|         if (url.startsWith('/')) { | ||||
|             return mainUrl + url | ||||
|         } | ||||
|         return "$mainUrl/$url" | ||||
|  | @ -176,7 +177,10 @@ interface LoadResponse { | |||
|     val posterUrl: String? | ||||
|     val year: Int? | ||||
|     val plot: String? | ||||
|     val rating : Int? // 0-100 | ||||
|     val rating: Int? // 0-100 | ||||
|     val tags: ArrayList<String>? | ||||
|     val duration: String? | ||||
|     val trailerUrl: String? | ||||
| } | ||||
| 
 | ||||
| fun LoadResponse?.isEpisodeBased(): Boolean { | ||||
|  | @ -189,7 +193,7 @@ fun LoadResponse?.isAnimeBased(): Boolean { | |||
|     return (this.type == TvType.Anime || this.type == TvType.ONA) // && (this is AnimeLoadResponse) | ||||
| } | ||||
| 
 | ||||
| data class AnimeEpisode(val url: String, val name : String? = null) | ||||
| data class AnimeEpisode(val url: String, val name: String? = null) | ||||
| 
 | ||||
| data class AnimeLoadResponse( | ||||
|     val engName: String?, | ||||
|  | @ -207,12 +211,14 @@ data class AnimeLoadResponse( | |||
|     val showStatus: ShowStatus?, | ||||
| 
 | ||||
|     override val plot: String?, | ||||
|     val tags: ArrayList<String>? = null, | ||||
|     override val tags: ArrayList<String>? = null, | ||||
|     val synonyms: ArrayList<String>? = null, | ||||
| 
 | ||||
|     val malId: Int? = null, | ||||
|     val anilistId: Int? = null, | ||||
|     override val rating: Int? = null, | ||||
|     override val duration: String? = null, | ||||
|     override val trailerUrl: String? = null, | ||||
| ) : LoadResponse | ||||
| 
 | ||||
| data class MovieLoadResponse( | ||||
|  | @ -226,11 +232,23 @@ data class MovieLoadResponse( | |||
|     override val year: Int?, | ||||
|     override val plot: String?, | ||||
| 
 | ||||
|     val imdbId: Int?, | ||||
|     val imdbUrl: String?, | ||||
|     override val rating: Int? = null, | ||||
|     override val tags: ArrayList<String>? = null, | ||||
|     override val duration: String? = null, | ||||
|     override val trailerUrl: String? = null, | ||||
| ) : LoadResponse | ||||
| 
 | ||||
| data class TvSeriesEpisode(val name: String?, val season: Int?, val episode: Int?, val data: String) | ||||
| data class TvSeriesEpisode( | ||||
|     val name: String?, | ||||
|     val season: Int?, | ||||
|     val episode: Int?, | ||||
|     val data: String, | ||||
|     val posterUrl: String? = null, | ||||
|     val date: String? = null, | ||||
|     val rating: Int? = null, | ||||
|     val descript: String? = null, | ||||
| ) | ||||
| 
 | ||||
| data class TvSeriesLoadResponse( | ||||
|     override val name: String, | ||||
|  | @ -244,6 +262,9 @@ data class TvSeriesLoadResponse( | |||
|     override val plot: String?, | ||||
| 
 | ||||
|     val showStatus: ShowStatus?, | ||||
|     val imdbId: Int?, | ||||
|     val imdbUrl: String?, | ||||
|     override val rating: Int? = null, | ||||
|     override val tags: ArrayList<String>? = null, | ||||
|     override val duration: String? = null, | ||||
|     override val trailerUrl: String? = null, | ||||
| ) : LoadResponse | ||||
|  | @ -6,9 +6,11 @@ import com.lagradost.cloudstream3.* | |||
| import com.lagradost.cloudstream3.APIHolder.unixTime | ||||
| import com.lagradost.cloudstream3.utils.ExtractorLink | ||||
| import com.lagradost.cloudstream3.utils.Qualities | ||||
| import com.lagradost.cloudstream3.utils.extractors.M3u8Manifest | ||||
| import com.lagradost.cloudstream3.utils.getQualityFromName | ||||
| import org.jsoup.Jsoup | ||||
| 
 | ||||
| //BE AWARE THAT weboas.is is a clone of lookmovie | ||||
| class LookMovieProvider : MainAPI() { | ||||
|     override val hasQuickSearch: Boolean | ||||
|         get() = true | ||||
|  | @ -62,7 +64,7 @@ class LookMovieProvider : MainAPI() { | |||
|         @JsonProperty("season") var season: String, | ||||
|     ) | ||||
| 
 | ||||
|     override fun quickSearch(query: String): ArrayList<SearchResponse>? { | ||||
|     override fun quickSearch(query: String): ArrayList<SearchResponse> { | ||||
|         val movieUrl = "$mainUrl/api/v1/movies/search/?q=$query" | ||||
|         val movieResponse = khttp.get(movieUrl) | ||||
|         val movies = mapper.readValue<LookMovieSearchResultRoot>(movieResponse.text).result | ||||
|  | @ -138,12 +140,13 @@ class LookMovieProvider : MainAPI() { | |||
| 
 | ||||
|     override fun loadLinks(data: String, isCasting: Boolean, callback: (ExtractorLink) -> Unit): Boolean { | ||||
|         val response = khttp.get(data.replace("\$unixtime", unixTime.toString())) | ||||
| 
 | ||||
|         "\"(.*?)\":\"(.*?)\"".toRegex().findAll(response.text).forEach { | ||||
|             var quality = it.groupValues[1].replace("auto", "Auto") | ||||
|             if (quality != "Auto") quality += "p" | ||||
|             val url = it.groupValues[2] | ||||
|             callback.invoke(ExtractorLink(this.name, "${this.name} - $quality", url, "", getQualityFromName(quality),true)) | ||||
|         M3u8Manifest.extractLinks(response.text).forEach { | ||||
|             callback.invoke(ExtractorLink(this.name, | ||||
|                 "${this.name} - ${it.second}", | ||||
|                 fixUrl(it.first), | ||||
|                 "", | ||||
|                 getQualityFromName(it.second), | ||||
|                 true)) | ||||
|         } | ||||
|         return true | ||||
|     } | ||||
|  | @ -174,9 +177,6 @@ class LookMovieProvider : MainAPI() { | |||
|         val root = mapper.readValue<LookMovieTokenRoot>(tokenResponse.text) | ||||
|         val accessToken = root.data?.accessToken ?: return null | ||||
| 
 | ||||
|         //https://lookmovie.io/api/v1/security/show-access?slug=9140554-loki-2021&token=&sk=null&step=1 | ||||
|         //https://lookmovie.io/api/v1/security/movie-access?id_movie=11582&token=1&sk=&step=1 | ||||
| 
 | ||||
|         if (isMovie) { | ||||
|             return MovieLoadResponse(name, | ||||
|                 slug, | ||||
|  | @ -198,10 +198,6 @@ class LookMovieProvider : MainAPI() { | |||
|                 return this.replace("$replace:", "\"$replace\":") | ||||
|             } | ||||
| 
 | ||||
|             //https://lookmovie.io/api/v1/security/show-access?slug=9140554-loki-2021&token=&sk=null&step=1 | ||||
|             //https://lookmovie.io/manifests/shows/json/TGv3dO0pcwomftMrywOnmw/1624571222/128848/master.m3u8 | ||||
|             //https://lookmovie.io/api/v1/shows/episode-subtitles/?id_episode=128848 | ||||
| 
 | ||||
|             val json = season | ||||
|                 .replace("\'", "\"") | ||||
|                 .fixSeasonJson("title") | ||||
|  | @ -230,6 +226,5 @@ class LookMovieProvider : MainAPI() { | |||
|                 null, | ||||
|                 rating) | ||||
|         } | ||||
|         //watch-heading | ||||
|     } | ||||
| } | ||||
|  | @ -109,7 +109,7 @@ class MeloMovieProvider : MainAPI() { | |||
|             return src.toRegex().find(response)?.groups?.get(1)?.value ?: return null | ||||
|         } | ||||
| 
 | ||||
|         val imdbId = findUsingRegex("var imdb = \"(tt[0-9]*)\"")?.toIntOrNull() | ||||
|         val imdbUrl = findUsingRegex("var imdb = \"(.*?)\"") | ||||
|         val document = Jsoup.parse(response) | ||||
|         val poster = document.selectFirst("img.img-fluid").attr("src") | ||||
|         val type = findUsingRegex("var posttype = ([0-9]*)")?.toInt() ?: return null | ||||
|  | @ -128,7 +128,7 @@ class MeloMovieProvider : MainAPI() { | |||
|                 poster, | ||||
|                 year, | ||||
|                 plot, | ||||
|                 imdbId) | ||||
|                 imdbUrl) | ||||
|         } else if (type == 2) { | ||||
|             val episodes = ArrayList<TvSeriesEpisode>() | ||||
|             val seasons = document.select("div.accordion__card") | ||||
|  | @ -154,7 +154,7 @@ class MeloMovieProvider : MainAPI() { | |||
|                 year, | ||||
|                 plot, | ||||
|                 null, | ||||
|                 imdbId) | ||||
|                 imdbUrl) | ||||
|         } | ||||
|         return null | ||||
|     } | ||||
|  |  | |||
|  | @ -0,0 +1,176 @@ | |||
| package com.lagradost.cloudstream3.movieproviders | ||||
| 
 | ||||
| import com.lagradost.cloudstream3.* | ||||
| import com.lagradost.cloudstream3.utils.ExtractorLink | ||||
| import com.lagradost.cloudstream3.utils.Qualities | ||||
| import org.jsoup.Jsoup | ||||
| 
 | ||||
| // referer = https://trailers.to, USERAGENT ALSO REQUIRED | ||||
| class TrailersToProvider : MainAPI() { | ||||
|     override val mainUrl: String | ||||
|         get() = "https://trailers.to" | ||||
|     override val name: String | ||||
|         get() = "Trailers.to" | ||||
| 
 | ||||
|     override val hasQuickSearch: Boolean | ||||
|         get() = true | ||||
| 
 | ||||
|     override fun quickSearch(query: String): ArrayList<SearchResponse> { | ||||
|         val url = "https://trailers.to/en/quick-search?q=$query" | ||||
|         val response = khttp.get(url) | ||||
|         val document = Jsoup.parse(response.text) | ||||
|         val items = document.select("div.group-post-minimal > a.post-minimal") | ||||
|         if (items.isNullOrEmpty()) return ArrayList() | ||||
| 
 | ||||
|         val returnValue = ArrayList<SearchResponse>() | ||||
|         for (item in items) { | ||||
|             val href = fixUrl(item.attr("href")) | ||||
|             val poster = item.selectFirst("> div.post-minimal-media > img").attr("src") | ||||
|             val header = item.selectFirst("> div.post-minimal-main") | ||||
|             val name = header.selectFirst("> span.link-black").text() | ||||
|             val info = header.select("> p") | ||||
|             val year = info?.get(1)?.text()?.toIntOrNull() | ||||
|             val isTvShow = href.contains("/tvshow/") | ||||
| 
 | ||||
|             returnValue.add( | ||||
|                 if (isTvShow) { | ||||
|                     TvSeriesSearchResponse(name, href, href, this.name, TvType.TvSeries, poster, year, null) | ||||
|                 } else { | ||||
|                     MovieSearchResponse(name, href, href, this.name, TvType.Movie, poster, year) | ||||
|                 } | ||||
|             ) | ||||
|         } | ||||
|         return returnValue | ||||
|     } | ||||
| 
 | ||||
|     override fun search(query: String): ArrayList<SearchResponse> { | ||||
|         val url = "https://trailers.to/en/popular/movies-tvshows-collections?q=$query" | ||||
|         val response = khttp.get(url) | ||||
|         val document = Jsoup.parse(response.text) | ||||
|         val items = document.select("div.col-lg-8 > article.list-item") | ||||
|         if (items.isNullOrEmpty()) return ArrayList() | ||||
|         val returnValue = ArrayList<SearchResponse>() | ||||
|         for (item in items) { | ||||
|             val poster = item.selectFirst("> div.tour-modern-media > a.tour-modern-figure > img").attr("src") | ||||
|             val infoDiv = item.selectFirst("> div.tour-modern-main") | ||||
|             val nameHeader = infoDiv.select("> h5.tour-modern-title > a").last() | ||||
|             val name = nameHeader.text() | ||||
|             val href = fixUrl(nameHeader.attr("href")) | ||||
|             val year = infoDiv.selectFirst("> div > span.small-text")?.text()?.takeLast(4)?.toIntOrNull() | ||||
|             val isTvShow = href.contains("/tvshow/") | ||||
| 
 | ||||
|             returnValue.add( | ||||
|                 if (isTvShow) { | ||||
|                     TvSeriesSearchResponse(name, href, href, this.name, TvType.TvSeries, poster, year, null) | ||||
|                 } else { | ||||
|                     MovieSearchResponse(name, href, href, this.name, TvType.Movie, poster, year) | ||||
|                 } | ||||
|             ) | ||||
|         } | ||||
|         return returnValue | ||||
|     } | ||||
| 
 | ||||
|     private fun loadLink(data: String, callback: (ExtractorLink) -> Unit): Boolean { | ||||
|         val response = khttp.get(data) | ||||
|         val url = "<source src='(.*?)'".toRegex().find(response.text)?.groupValues?.get(1) | ||||
|         if (url != null) { | ||||
|             callback.invoke(ExtractorLink(this.name, this.name, url, mainUrl, Qualities.Unknown.value, false)) | ||||
|             return true | ||||
|         } | ||||
|         return false | ||||
|     } | ||||
| 
 | ||||
|     override fun loadLinks(data: String, isCasting: Boolean, callback: (ExtractorLink) -> Unit): Boolean { | ||||
|         if (isCasting) return false | ||||
|         val isMovie = data.contains("/web-sources/") | ||||
|         if (isMovie) { | ||||
|             return loadLink(data, callback) | ||||
|         } else if (data.contains("/episode/")) { | ||||
|             val response = khttp.get(data) | ||||
|             val document = Jsoup.parse(response.text) | ||||
|             val subData = fixUrl(document.selectFirst("content").attr("data-url") ?: return false) | ||||
|             if (subData.contains("/web-sources/")) { | ||||
|                 return loadLink(subData, callback) | ||||
|             } | ||||
|         } | ||||
|         return false | ||||
|     } | ||||
| 
 | ||||
|     override fun load(slug: String): LoadResponse? { | ||||
|         val response = khttp.get(slug) | ||||
|         val document = Jsoup.parse(response.text) | ||||
|         val metaInfo = document.select("div.post-info-meta > ul.post-info-meta-list > li") | ||||
|         val year = metaInfo?.get(0)?.selectFirst("> span.small-text")?.text()?.takeLast(4)?.toIntOrNull() | ||||
|         val rating = parseRating(metaInfo?.get(1)?.selectFirst("> span.small-text")?.text()?.replace("/ 10", "")) | ||||
|         val duration = metaInfo?.get(2)?.selectFirst("> span.small-text")?.text() | ||||
|         val imdbUrl = metaInfo?.get(3)?.selectFirst("> a")?.attr("href") | ||||
|         val trailer = metaInfo?.get(4)?.selectFirst("> a")?.attr("href") | ||||
|         val poster = document.selectFirst("div.slider-image > a > img").attr("src") | ||||
|         val descriptHeader = document.selectFirst("article.post-info") | ||||
|         var title = document.selectFirst("h2.breadcrumbs-custom-title > a").text() | ||||
|         title = title.substring(0, title.length - 6) // REMOVE YEAR | ||||
| 
 | ||||
|         val descript = descriptHeader.select("> div > p").text() | ||||
|         val table = descriptHeader.select("> table.post-info-table > tbody > tr > td") | ||||
|         var generes: List<String>? = null | ||||
|         for (i in 0 until table.size / 2) { | ||||
|             val header = table[i * 2].text() | ||||
|             val info = table[i * 2 + 1] | ||||
|             when (header) { | ||||
|                 "Genre" -> generes = info.text().split(",") | ||||
|             } | ||||
|         } | ||||
|         val tags = if (generes == null) null else ArrayList(generes) | ||||
| 
 | ||||
|         val isTvShow = slug.contains("/tvshow/") | ||||
|         if (isTvShow) { | ||||
|             val episodes = document.select("article.tour-modern") ?: return null | ||||
|             val parsedEpisodes = episodes.map { item -> | ||||
|                 val epPoster = item.selectFirst("> div.tour-modern-media > a.tour-modern-figure > img").attr("src") | ||||
|                 val main = item.selectFirst("> div.tour-modern-main") | ||||
|                 val titleHeader = main.selectFirst("> h5.tour-modern-title > a") | ||||
|                 val titleName = titleHeader.text() | ||||
|                 val href = fixUrl(titleHeader.attr("href")) | ||||
|                 val gValues = ".*?Season ([0-9]*).*Episode ([0-9]*): (.*)".toRegex().find(titleName)?.groupValues | ||||
|                 val season = gValues?.get(1)?.toIntOrNull() | ||||
|                 val episode = gValues?.get(2)?.toIntOrNull() | ||||
|                 val epName = gValues?.get(3) | ||||
|                 val infoHeaders = main.select("> div > span.small-text") | ||||
|                 val date = infoHeaders?.get(0)?.text() | ||||
|                 val ratingText = infoHeaders?.get(1)?.text()?.replace("/ 10", "") | ||||
|                 val epRating = if (ratingText == null) null else parseRating(ratingText) | ||||
|                 val epDescript = main.selectFirst("> p")?.text() | ||||
|                 TvSeriesEpisode(epName, season, episode, href, epPoster, date, epRating, epDescript) | ||||
|             } | ||||
|             return TvSeriesLoadResponse(title, | ||||
|                 slug, | ||||
|                 this.name, | ||||
|                 TvType.TvSeries, | ||||
|                 ArrayList(parsedEpisodes), | ||||
|                 poster, | ||||
|                 year, | ||||
|                 descript, | ||||
|                 null, | ||||
|                 imdbUrl, | ||||
|                 rating, | ||||
|                 tags, | ||||
|                 duration, | ||||
|                 trailer) | ||||
|         } else { | ||||
|             val data = fixUrl(document.selectFirst("content").attr("data-url") ?: return null) | ||||
|             return MovieLoadResponse(title, | ||||
|                 slug, | ||||
|                 this.name, | ||||
|                 TvType.Movie, | ||||
|                 data, | ||||
|                 poster, | ||||
|                 year, | ||||
|                 descript, | ||||
|                 imdbUrl, | ||||
|                 rating, | ||||
|                 tags, | ||||
|                 duration, | ||||
|                 trailer) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -483,6 +483,21 @@ activity?.startActivityForResult(vlcIntent, REQUEST_CODE) | |||
|                         result_tag_holder.visibility = GONE | ||||
|                         result_status.visibility = GONE | ||||
| 
 | ||||
|                         val tags = d.tags | ||||
|                         if (tags == null) { | ||||
|                             result_tag_holder.visibility = GONE | ||||
|                         } else { | ||||
|                             result_tag_holder.visibility = VISIBLE | ||||
| 
 | ||||
|                             for ((index, tag) in tags.withIndex()) { | ||||
|                                 val viewBtt = layoutInflater.inflate(R.layout.result_tag, null) | ||||
|                                 val btt = viewBtt.findViewById<MaterialButton>(R.id.result_tag_card) | ||||
|                                 btt.text = tag | ||||
| 
 | ||||
|                                 result_tag.addView(viewBtt, index) | ||||
|                             } | ||||
|                         } | ||||
| 
 | ||||
|                         when (d.type) { | ||||
|                             TvType.Movie -> { | ||||
|                                 result_play_movie.visibility = VISIBLE | ||||
|  | @ -525,20 +540,6 @@ activity?.startActivityForResult(vlcIntent, REQUEST_CODE) | |||
|                                 val titleName = d.name | ||||
|                                 result_title.text = titleName | ||||
|                                 result_toolbar.title = titleName | ||||
| 
 | ||||
|                                 if (d.tags == null) { | ||||
|                                     result_tag_holder.visibility = GONE | ||||
|                                 } else { | ||||
|                                     result_tag_holder.visibility = VISIBLE | ||||
| 
 | ||||
|                                     for ((index, tag) in d.tags.withIndex()) { | ||||
|                                         val viewBtt = layoutInflater.inflate(R.layout.result_tag, null) | ||||
|                                         val btt = viewBtt.findViewById<MaterialButton>(R.id.result_tag_card) | ||||
|                                         btt.text = tag | ||||
| 
 | ||||
|                                         result_tag.addView(viewBtt, index) | ||||
|                                     } | ||||
|                                 } | ||||
|                             } | ||||
|                             else -> result_title.text = d.name | ||||
|                         } | ||||
|  |  | |||
|  | @ -0,0 +1,16 @@ | |||
| package com.lagradost.cloudstream3.utils.extractors | ||||
| 
 | ||||
| //{"auto":"/manifests/movies/15559/1624728920/qDwu5BOsfAwfTmnnjmkmXA/master.m3u8","1080p":"https://vdoc3.sallenes.space/qDwu5BOsfAwfTmnnjmkmXA/1624728920/storage6/movies/the-man-with-the-iron-heart-2017/1080p/index.m3u8","720p":"https://vdoc3.sallenes.space/qDwu5BOsfAwfTmnnjmkmXA/1624728920/storage6/movies/the-man-with-the-iron-heart-2017/720p/index.m3u8","360p":"https://vdoc3.sallenes.space/qDwu5BOsfAwfTmnnjmkmXA/1624728920/storage6/movies/the-man-with-the-iron-heart-2017/360p/index.m3u8","480p":"https://vdoc3.sallenes.space/qDwu5BOsfAwfTmnnjmkmXA/1624728920/storage6/movies/the-man-with-the-iron-heart-2017/480p/index.m3u8"} | ||||
| object M3u8Manifest { | ||||
|     // URL = first, QUALITY = second | ||||
|     fun extractLinks(m3u8Data: String): ArrayList<Pair<String, String>> { | ||||
|         val data: ArrayList<Pair<String, String>> = ArrayList() | ||||
|         "\"(.*?)\":\"(.*?)\"".toRegex().findAll(m3u8Data).forEach { | ||||
|             var quality = it.groupValues[1].replace("auto", "Auto") | ||||
|             if (quality != "Auto") quality += "p" | ||||
|             val url = it.groupValues[2] | ||||
|             data.add(Pair(url, quality)) | ||||
|         } | ||||
|         return data | ||||
|     } | ||||
| } | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue