forked from recloudstream/cloudstream
		
	fixed search, made stuff more parallel
This commit is contained in:
		
							parent
							
								
									b0cadda315
								
							
						
					
					
						commit
						e0d9171f3e
					
				
					 26 changed files with 312 additions and 221 deletions
				
			
		|  | @ -115,13 +115,13 @@ object APIHolder { | |||
|     } | ||||
| 
 | ||||
|     fun Context.getApiSettings(): HashSet<String> { | ||||
|         val settingsManager = PreferenceManager.getDefaultSharedPreferences(this) | ||||
|         //val settingsManager = PreferenceManager.getDefaultSharedPreferences(this) | ||||
| 
 | ||||
|         val hashSet = HashSet<String>() | ||||
|         val activeLangs = getApiProviderLangSettings() | ||||
|         hashSet.addAll(apis.filter { activeLangs.contains(it.lang) }.map { it.name }) | ||||
| 
 | ||||
|         val set = settingsManager.getStringSet( | ||||
|         /*val set = settingsManager.getStringSet( | ||||
|             this.getString(R.string.search_providers_list_key), | ||||
|             hashSet | ||||
|         )?.toHashSet() ?: hashSet | ||||
|  | @ -132,9 +132,10 @@ object APIHolder { | |||
|             if (activeLangs.contains(api.lang)) { | ||||
|                 list.add(name) | ||||
|             } | ||||
|         } | ||||
|         if (list.isEmpty()) return hashSet | ||||
|         return list | ||||
|         }*/ | ||||
|         //if (list.isEmpty()) return hashSet | ||||
|         //return list | ||||
|         return hashSet | ||||
|     } | ||||
| 
 | ||||
|     fun Context.getApiDubstatusSettings(): HashSet<DubStatus> { | ||||
|  |  | |||
|  | @ -1,5 +1,6 @@ | |||
| package com.lagradost.cloudstream3 | ||||
| 
 | ||||
| import com.lagradost.cloudstream3.mvvm.logError | ||||
| import kotlinx.coroutines.async | ||||
| import kotlinx.coroutines.runBlocking | ||||
| 
 | ||||
|  | @ -43,8 +44,17 @@ fun <A, B> List<A>.apmap(f: suspend (A) -> B): List<B> = runBlocking { | |||
|     exec.awaitTermination(1, TimeUnit.DAYS) | ||||
| }*/ | ||||
| 
 | ||||
| // built in try catch | ||||
| fun <R> argamap( | ||||
|     vararg transforms: suspend () -> R, | ||||
| ) = runBlocking { | ||||
|     transforms.map { async { it.invoke() } }.map { it.await() } | ||||
|     transforms.map { | ||||
|         async { | ||||
|             try { | ||||
|                 it.invoke() | ||||
|             } catch (e: Exception) { | ||||
|                 logError(e) | ||||
|             } | ||||
|         } | ||||
|     }.map { it.await() } | ||||
| } | ||||
|  | @ -258,7 +258,7 @@ class AllAnimeProvider : MainAPI() { | |||
| 
 | ||||
|         val sources = Regex("""sourceUrl[:=]"(.+?)"""").findAll(html).toList() | ||||
|             .map { URLDecoder.decode(it.destructured.component1().sanitize(), "UTF-8") } | ||||
|         sources.forEach { | ||||
|         sources.apmap { | ||||
|             var link = it | ||||
|             if (URI(link).isAbsolute || link.startsWith("//")) { | ||||
|                 if (link.startsWith("//")) link = "https:$it" | ||||
|  |  | |||
|  | @ -95,9 +95,7 @@ class AnimeFlickProvider : MainAPI() { | |||
|             var alreadyAdded = false | ||||
|             for (extractor in extractorApis) { | ||||
|                 if (link.startsWith(extractor.mainUrl)) { | ||||
|                     extractor.getSafeUrl(link, data)?.forEach { | ||||
|                         callback(it) | ||||
|                     } | ||||
|                     extractor.getSafeUrl(link, data)?.forEach(callback) | ||||
|                     alreadyAdded = true | ||||
|                     break | ||||
|                 } | ||||
|  |  | |||
|  | @ -52,8 +52,11 @@ class WcoProvider : MainAPI() { | |||
|                     val nameHeader = filmDetail.selectFirst("> h3.film-name > a") | ||||
|                     val title = nameHeader.text().replace(" (Dub)", "") | ||||
|                     val href = | ||||
|                         nameHeader.attr("href").replace("/watch/", "/anime/").replace("-episode-.*".toRegex(), "/") | ||||
|                     val isDub = filmPoster.selectFirst("> div.film-poster-quality")?.text()?.contains("DUB") ?: false | ||||
|                         nameHeader.attr("href").replace("/watch/", "/anime/") | ||||
|                             .replace("-episode-.*".toRegex(), "/") | ||||
|                     val isDub = | ||||
|                         filmPoster.selectFirst("> div.film-poster-quality")?.text()?.contains("DUB") | ||||
|                             ?: false | ||||
|                     val poster = filmPoster.selectFirst("> img").attr("data-src") | ||||
|                     val set: EnumSet<DubStatus> = | ||||
|                         EnumSet.of(if (isDub) DubStatus.Dubbed else DubStatus.Subbed) | ||||
|  | @ -84,8 +87,11 @@ class WcoProvider : MainAPI() { | |||
|             val img = fixUrl(i.selectFirst("img").attr("data-src")) | ||||
|             val title = i.selectFirst("img").attr("title") | ||||
|             val isDub = !i.select(".pick.film-poster-quality").isEmpty() | ||||
|             val year = i.selectFirst(".film-detail.film-detail-fix > div > span:nth-child(1)").text().toIntOrNull() | ||||
|             val type = i.selectFirst(".film-detail.film-detail-fix > div > span:nth-child(3)").text() | ||||
|             val year = | ||||
|                 i.selectFirst(".film-detail.film-detail-fix > div > span:nth-child(1)").text() | ||||
|                     .toIntOrNull() | ||||
|             val type = | ||||
|                 i.selectFirst(".film-detail.film-detail-fix > div > span:nth-child(3)").text() | ||||
| 
 | ||||
|             returnValue.add( | ||||
|                 if (getType(type) == TvType.AnimeMovie) { | ||||
|  | @ -174,8 +180,9 @@ class WcoProvider : MainAPI() { | |||
|         val response = app.get(url, timeout = 120).text | ||||
|         val document = Jsoup.parse(response) | ||||
| 
 | ||||
|         val japaneseTitle = document.selectFirst("div.elements div.row > div:nth-child(1) > div.row-line:nth-child(1)") | ||||
|             ?.text()?.trim()?.replace("Other names:", "")?.trim() | ||||
|         val japaneseTitle = | ||||
|             document.selectFirst("div.elements div.row > div:nth-child(1) > div.row-line:nth-child(1)") | ||||
|                 ?.text()?.trim()?.replace("Other names:", "")?.trim() | ||||
| 
 | ||||
|         val canonicalTitle = document.selectFirst("meta[name=\"title\"]") | ||||
|             ?.attr("content")?.split("| W")?.get(0).toString() | ||||
|  | @ -187,22 +194,25 @@ class WcoProvider : MainAPI() { | |||
|             AnimeEpisode(it.attr("href")) | ||||
|         } ?: ArrayList()) | ||||
| 
 | ||||
|         val statusElem = document.selectFirst("div.elements div.row > div:nth-child(1) > div.row-line:nth-child(2)") | ||||
|         val statusElem = | ||||
|             document.selectFirst("div.elements div.row > div:nth-child(1) > div.row-line:nth-child(2)") | ||||
|         val status = when (statusElem?.text()?.replace("Status:", "")?.trim()) { | ||||
|             "Ongoing" -> ShowStatus.Ongoing | ||||
|             "Completed" -> ShowStatus.Completed | ||||
|             else -> null | ||||
|         } | ||||
|         val yearText = | ||||
|             document.selectFirst("div.elements div.row > div:nth-child(2) > div.row-line:nth-child(4)")?.text() | ||||
|             document.selectFirst("div.elements div.row > div:nth-child(2) > div.row-line:nth-child(4)") | ||||
|                 ?.text() | ||||
|         val year = yearText?.replace("Date release:", "")?.trim()?.split("-")?.get(0)?.toIntOrNull() | ||||
| 
 | ||||
|         val poster = document.selectFirst(".film-poster-img")?.attr("src") | ||||
|         val type = document.selectFirst("span.item.mr-1 > a")?.text()?.trim() | ||||
| 
 | ||||
|         val synopsis = document.selectFirst(".description > p")?.text()?.trim() | ||||
|         val genre = document.select("div.elements div.row > div:nth-child(1) > div.row-line:nth-child(5) > a") | ||||
|             .map { it?.text()?.trim().toString() } | ||||
|         val genre = | ||||
|             document.select("div.elements div.row > div:nth-child(1) > div.row-line:nth-child(5) > a") | ||||
|                 .map { it?.text()?.trim().toString() } | ||||
| 
 | ||||
|         return newAnimeLoadResponse(canonicalTitle, url, getType(type ?: "")) { | ||||
|             japName = japaneseTitle | ||||
|  | @ -231,9 +241,7 @@ class WcoProvider : MainAPI() { | |||
|         } | ||||
| 
 | ||||
|         for (server in servers) { | ||||
|             WcoStream().getSafeUrl(server["link"].toString(), "")?.forEach { | ||||
|                 callback.invoke(it) | ||||
|             } | ||||
|             WcoStream().getSafeUrl(server["link"].toString(), "")?.forEach(callback) | ||||
|         } | ||||
|         return true | ||||
|     } | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ package com.lagradost.cloudstream3.extractors | |||
| 
 | ||||
| import com.lagradost.cloudstream3.apmap | ||||
| import com.lagradost.cloudstream3.app | ||||
| import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall | ||||
| import com.lagradost.cloudstream3.argamap | ||||
| import com.lagradost.cloudstream3.utils.ExtractorLink | ||||
| import com.lagradost.cloudstream3.utils.extractorApis | ||||
| import com.lagradost.cloudstream3.utils.getQualityFromName | ||||
|  | @ -27,17 +27,22 @@ class Vidstream(val mainUrl: String) { | |||
|     private val normalApis = arrayListOf(MultiQuality()) | ||||
| 
 | ||||
|     // https://gogo-stream.com/streaming.php?id=MTE3NDg5 | ||||
|     suspend fun getUrl(id: String, isCasting: Boolean = false, callback: (ExtractorLink) -> Unit): Boolean { | ||||
|         try { | ||||
|             normalApis.apmap { api -> | ||||
|                 val url = api.getExtractorUrl(id) | ||||
|                 val source = api.getSafeUrl(url) | ||||
|                 source?.forEach { callback.invoke(it) } | ||||
|             } | ||||
|             val extractorUrl = getExtractorUrl(id) | ||||
| 
 | ||||
|             /** Stolen from GogoanimeProvider.kt extractor */ | ||||
|             suspendSafeApiCall { | ||||
|     suspend fun getUrl( | ||||
|         id: String, | ||||
|         isCasting: Boolean = false, | ||||
|         callback: (ExtractorLink) -> Unit | ||||
|     ): Boolean { | ||||
|         println("VIDSTREAM:: $id") | ||||
|         val extractorUrl = getExtractorUrl(id) | ||||
|         argamap( | ||||
|             { | ||||
|                 normalApis.apmap { api -> | ||||
|                     val url = api.getExtractorUrl(id) | ||||
|                     val source = api.getSafeUrl(url) | ||||
|                     source?.forEach { callback.invoke(it) } | ||||
|                 } | ||||
|             }, { | ||||
|                 /** Stolen from GogoanimeProvider.kt extractor */ | ||||
|                 val link = getDownloadUrl(id) | ||||
|                 println("Generated vidstream download link: $link") | ||||
|                 val page = app.get(link, referer = extractorUrl) | ||||
|  | @ -50,7 +55,8 @@ class Vidstream(val mainUrl: String) { | |||
|                     val href = element.attr("href") ?: return@apmap | ||||
|                     val qual = if (element.text() | ||||
|                             .contains("HDP") | ||||
|                     ) "1080" else qualityRegex.find(element.text())?.destructured?.component1().toString() | ||||
|                     ) "1080" else qualityRegex.find(element.text())?.destructured?.component1() | ||||
|                         .toString() | ||||
| 
 | ||||
|                     if (!loadExtractor(href, link, callback)) { | ||||
|                         callback.invoke( | ||||
|  | @ -65,34 +71,32 @@ class Vidstream(val mainUrl: String) { | |||
|                         ) | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             }, { | ||||
|                 with(app.get(extractorUrl)) { | ||||
|                     val document = Jsoup.parse(this.text) | ||||
|                     val primaryLinks = document.select("ul.list-server-items > li.linkserver") | ||||
|                     //val extractedLinksList: MutableList<ExtractorLink> = mutableListOf() | ||||
| 
 | ||||
|             with(app.get(extractorUrl)) { | ||||
|                 val document = Jsoup.parse(this.text) | ||||
|                 val primaryLinks = document.select("ul.list-server-items > li.linkserver") | ||||
|                 //val extractedLinksList: MutableList<ExtractorLink> = mutableListOf() | ||||
|                     // All vidstream links passed to extractors | ||||
|                     primaryLinks.distinctBy { it.attr("data-video") }.forEach { element -> | ||||
|                         val link = element.attr("data-video") | ||||
|                         //val name = element.text() | ||||
| 
 | ||||
|                 // All vidstream links passed to extractors | ||||
|                 primaryLinks.distinctBy { it.attr("data-video") }.forEach { element -> | ||||
|                     val link = element.attr("data-video") | ||||
|                     //val name = element.text() | ||||
| 
 | ||||
|                     // Matches vidstream links with extractors | ||||
|                     extractorApis.filter { !it.requiresReferer || !isCasting }.apmap { api -> | ||||
|                         if (link.startsWith(api.mainUrl)) { | ||||
|                             val extractedLinks = api.getSafeUrl(link, extractorUrl) | ||||
|                             if (extractedLinks?.isNotEmpty() == true) { | ||||
|                                 extractedLinks.forEach { | ||||
|                                     callback.invoke(it) | ||||
|                         // Matches vidstream links with extractors | ||||
|                         extractorApis.filter { !it.requiresReferer || !isCasting }.apmap { api -> | ||||
|                             if (link.startsWith(api.mainUrl)) { | ||||
|                                 val extractedLinks = api.getSafeUrl(link, extractorUrl) | ||||
|                                 if (extractedLinks?.isNotEmpty() == true) { | ||||
|                                     extractedLinks.forEach { | ||||
|                                         callback.invoke(it) | ||||
|                                     } | ||||
|                                 } | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 return true | ||||
|             } | ||||
|         } catch (e: Exception) { | ||||
|             return false | ||||
|         } | ||||
|         ) | ||||
|         return true | ||||
|     } | ||||
| } | ||||
|  | @ -1,9 +1,8 @@ | |||
| package com.lagradost.cloudstream3.extractors | ||||
| 
 | ||||
| import com.fasterxml.jackson.annotation.JsonProperty | ||||
| import com.fasterxml.jackson.module.kotlin.readValue | ||||
| import com.lagradost.cloudstream3.app | ||||
| import com.lagradost.cloudstream3.mapper | ||||
| import com.lagradost.cloudstream3.utils.AppUtils.parseJson | ||||
| import com.lagradost.cloudstream3.utils.ExtractorApi | ||||
| import com.lagradost.cloudstream3.utils.ExtractorLink | ||||
| import com.lagradost.cloudstream3.utils.getQualityFromName | ||||
|  | @ -29,14 +28,14 @@ open class VoeExtractor : ExtractorApi() { | |||
|                 .replace("0,", "0") | ||||
|                 .trim() | ||||
|             //Log.i(this.name, "Result => (src) ${src}") | ||||
|             mapper.readValue<ResponseLinks?>(src)?.let { voelink -> | ||||
|             parseJson<ResponseLinks?>(src)?.let { voelink -> | ||||
|                 //Log.i(this.name, "Result => (voelink) ${voelink}") | ||||
|                 val linkUrl = voelink.url | ||||
|                 val linkLabel = voelink.label?.toString() ?: "" | ||||
|                 if (!linkUrl.isNullOrEmpty()) { | ||||
|                     extractedLinksList.add( | ||||
|                         ExtractorLink( | ||||
|                             name = "Voe ${linkLabel}", | ||||
|                             name = "Voe $linkLabel", | ||||
|                             source = this.name, | ||||
|                             url = linkUrl, | ||||
|                             quality = getQualityFromName(linkLabel), | ||||
|  |  | |||
|  | @ -2,8 +2,11 @@ package com.lagradost.cloudstream3.movieproviders | |||
| 
 | ||||
| import com.fasterxml.jackson.module.kotlin.readValue | ||||
| import com.lagradost.cloudstream3.* | ||||
| import com.lagradost.cloudstream3.utils.* | ||||
| import com.lagradost.cloudstream3.utils.AppUtils.toJson | ||||
| import com.lagradost.cloudstream3.utils.ExtractorLink | ||||
| import com.lagradost.cloudstream3.utils.Qualities | ||||
| import com.lagradost.cloudstream3.utils.getPostForm | ||||
| import com.lagradost.cloudstream3.utils.loadExtractor | ||||
| import okio.Buffer | ||||
| import org.jsoup.Jsoup | ||||
| import org.jsoup.nodes.Document | ||||
|  | @ -211,14 +214,7 @@ class AllMoviesForYouProvider : MainAPI() { | |||
|                         } | ||||
|                     } | ||||
|                 } else if (requestUrl.startsWith("https://dood")) { | ||||
|                     for (extractor in extractorApis) { | ||||
|                         if (requestUrl.startsWith(extractor.mainUrl)) { | ||||
|                             extractor.getSafeUrl(requestUrl)?.forEach { link -> | ||||
|                                 callback(link) | ||||
|                             } | ||||
|                             break | ||||
|                         } | ||||
|                     } | ||||
|                     loadExtractor(requestUrl, null, callback) | ||||
|                 } else { | ||||
|                     callback( | ||||
|                         ExtractorLink( | ||||
|  |  | |||
|  | @ -1,10 +1,11 @@ | |||
| package com.lagradost.cloudstream3.movieproviders | ||||
| 
 | ||||
| import com.lagradost.cloudstream3.* | ||||
| import com.lagradost.cloudstream3.utils.* | ||||
| import com.lagradost.cloudstream3.utils.ExtractorLink | ||||
| import com.lagradost.cloudstream3.utils.extractorApis | ||||
| import java.util.* | ||||
| 
 | ||||
| class CinecalidadProvider:MainAPI() { | ||||
| class CinecalidadProvider : MainAPI() { | ||||
|     override val mainUrl = "https://cinecalidad.lol" | ||||
|     override val name = "Cinecalidad" | ||||
|     override val lang = "es" | ||||
|  | @ -15,6 +16,7 @@ class CinecalidadProvider:MainAPI() { | |||
|         TvType.Movie, | ||||
|         TvType.TvSeries, | ||||
|     ) | ||||
| 
 | ||||
|     override suspend fun getMainPage(): HomePageResponse { | ||||
|         val items = ArrayList<HomePageList>() | ||||
|         val urls = listOf( | ||||
|  | @ -22,18 +24,22 @@ class CinecalidadProvider:MainAPI() { | |||
|             Pair("$mainUrl/genero-de-la-pelicula/peliculas-en-calidad-4k/", "4K UHD"), | ||||
|         ) | ||||
| 
 | ||||
|         items.add(HomePageList("Series",app.get("$mainUrl/ver-serie/").document.select(".item.tvshows").map{ | ||||
|             val title = it.selectFirst("div.in_title").text() | ||||
|             TvSeriesSearchResponse( | ||||
|                 title, | ||||
|                 it.selectFirst("a").attr("href"), | ||||
|                 this.name, | ||||
|                 TvType.TvSeries, | ||||
|                 it.selectFirst(".poster.custom img").attr("data-src"), | ||||
|                 null, | ||||
|                 null, | ||||
|             ) | ||||
|         })) | ||||
|         items.add( | ||||
|             HomePageList( | ||||
|                 "Series", | ||||
|                 app.get("$mainUrl/ver-serie/").document.select(".item.tvshows").map { | ||||
|                     val title = it.selectFirst("div.in_title").text() | ||||
|                     TvSeriesSearchResponse( | ||||
|                         title, | ||||
|                         it.selectFirst("a").attr("href"), | ||||
|                         this.name, | ||||
|                         TvType.TvSeries, | ||||
|                         it.selectFirst(".poster.custom img").attr("data-src"), | ||||
|                         null, | ||||
|                         null, | ||||
|                     ) | ||||
|                 }) | ||||
|         ) | ||||
| 
 | ||||
|         for (i in urls) { | ||||
|             try { | ||||
|  | @ -100,7 +106,9 @@ class CinecalidadProvider:MainAPI() { | |||
|         val soup = app.get(url, timeout = 120).document | ||||
| 
 | ||||
|         val title = soup.selectFirst(".single_left h1").text() | ||||
|         val description = soup.selectFirst(".single_left > table:nth-child(3) > tbody:nth-child(1) > tr:nth-child(1) > td:nth-child(2) > p")?.text()?.trim() | ||||
|         val description = | ||||
|             soup.selectFirst(".single_left > table:nth-child(3) > tbody:nth-child(1) > tr:nth-child(1) > td:nth-child(2) > p") | ||||
|                 ?.text()?.trim() | ||||
|         val poster: String? = soup.selectFirst(".alignnone").attr("data-src") | ||||
|         val episodes = soup.select("div.se-c div.se-a ul.episodios li").map { li -> | ||||
|             val href = li.selectFirst("a").attr("href") | ||||
|  | @ -114,7 +122,8 @@ class CinecalidadProvider:MainAPI() { | |||
|                 epThumb | ||||
|             ) | ||||
|         } | ||||
|         return when (val tvType = if (url.contains("/ver-pelicula/")) TvType.Movie else TvType.TvSeries) { | ||||
|         return when (val tvType = | ||||
|             if (url.contains("/ver-pelicula/")) TvType.Movie else TvType.TvSeries) { | ||||
|             TvType.TvSeries -> { | ||||
|                 TvSeriesLoadResponse( | ||||
|                     title, | ||||
|  | @ -157,9 +166,10 @@ class CinecalidadProvider:MainAPI() { | |||
|             val urlserver = app.get(url).text | ||||
|             val serverRegex = Regex("(https:.*?\\\")") | ||||
|             val videos = serverRegex.findAll(urlserver).map { | ||||
|                 it.value.replace("\\/", "/").replace("\"","") | ||||
|                 it.value.replace("\\/", "/").replace("\"", "") | ||||
|             }.toList() | ||||
|             val serversRegex = Regex("(https?:\\/\\/(www\\.)?[-a-zA-Z0-9@:%._\\+~#=]{1,256}\\.[a-zA-Z0-9()]{1,6}\\b([-a-zA-Z0-9()@:%_\\+.~#?&\\/\\/=]*))") | ||||
|             val serversRegex = | ||||
|                 Regex("(https?:\\/\\/(www\\.)?[-a-zA-Z0-9@:%._\\+~#=]{1,256}\\.[a-zA-Z0-9()]{1,6}\\b([-a-zA-Z0-9()@:%_\\+.~#?&\\/\\/=]*))") | ||||
|             val links = serversRegex.findAll(videos.toString()).map { it.value }.toList() | ||||
|             for (link in links) { | ||||
|                 for (extractor in extractorApis) { | ||||
|  |  | |||
|  | @ -1,16 +1,14 @@ | |||
| package com.lagradost.cloudstream3.animeproviders | ||||
| 
 | ||||
| import com.lagradost.cloudstream3.* | ||||
| import java.util.* | ||||
| import com.lagradost.cloudstream3.extractors.FEmbed | ||||
| import com.lagradost.cloudstream3.utils.ExtractorLink | ||||
| import com.lagradost.cloudstream3.utils.loadExtractor | ||||
| import java.util.* | ||||
| import kotlin.collections.ArrayList | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| class DoramasYTProvider:MainAPI() { | ||||
| 
 | ||||
| class DoramasYTProvider : MainAPI() { | ||||
|     companion object { | ||||
|         fun getType(t: String): TvType { | ||||
|             return if (t.contains("OVA") || t.contains("Especial")) TvType.ONA | ||||
|  | @ -33,31 +31,44 @@ class DoramasYTProvider:MainAPI() { | |||
|     override suspend fun getMainPage(): HomePageResponse { | ||||
|         val urls = listOf( | ||||
|             Pair("$mainUrl/emision", "En emisión"), | ||||
|             Pair("$mainUrl/doramas?categoria=pelicula&genero=false&fecha=false&letra=false", "Peliculas"), | ||||
|             Pair( | ||||
|                 "$mainUrl/doramas?categoria=pelicula&genero=false&fecha=false&letra=false", | ||||
|                 "Peliculas" | ||||
|             ), | ||||
|             Pair("$mainUrl/doramas", "Doramas"), | ||||
|             Pair("$mainUrl/doramas?categoria=live-action&genero=false&fecha=false&letra=false", "Live Action"), | ||||
|             Pair( | ||||
|                 "$mainUrl/doramas?categoria=live-action&genero=false&fecha=false&letra=false", | ||||
|                 "Live Action" | ||||
|             ), | ||||
|         ) | ||||
| 
 | ||||
|         val items = ArrayList<HomePageList>() | ||||
| 
 | ||||
|         items.add(HomePageList("Capítulos actualizados", app.get(mainUrl, timeout = 120).document.select(".col-6").map{ | ||||
|             val title = it.selectFirst("p").text() | ||||
|             val poster = it.selectFirst(".chapter img").attr("src") | ||||
|             val epRegex = Regex("episodio-(\\d+)") | ||||
|             val url = it.selectFirst("a").attr("href").replace("ver/","dorama/").replace(epRegex,"sub-espanol") | ||||
|             val epNum = it.selectFirst("h3").text().toIntOrNull() | ||||
|             AnimeSearchResponse( | ||||
|                 title, | ||||
|                 url, | ||||
|                 this.name, | ||||
|                 TvType.Anime, | ||||
|                 poster, | ||||
|                 null, | ||||
|                 if (title.contains("Latino") || title.contains("Castellano")) EnumSet.of(DubStatus.Dubbed) else EnumSet.of(DubStatus.Subbed), | ||||
|                 subEpisodes = epNum, | ||||
|                 dubEpisodes = epNum, | ||||
|             ) | ||||
|         })) | ||||
|         items.add( | ||||
|             HomePageList( | ||||
|                 "Capítulos actualizados", | ||||
|                 app.get(mainUrl, timeout = 120).document.select(".col-6").map { | ||||
|                     val title = it.selectFirst("p").text() | ||||
|                     val poster = it.selectFirst(".chapter img").attr("src") | ||||
|                     val epRegex = Regex("episodio-(\\d+)") | ||||
|                     val url = it.selectFirst("a").attr("href").replace("ver/", "dorama/") | ||||
|                         .replace(epRegex, "sub-espanol") | ||||
|                     val epNum = it.selectFirst("h3").text().toIntOrNull() | ||||
|                     AnimeSearchResponse( | ||||
|                         title, | ||||
|                         url, | ||||
|                         this.name, | ||||
|                         TvType.Anime, | ||||
|                         poster, | ||||
|                         null, | ||||
|                         if (title.contains("Latino") || title.contains("Castellano")) EnumSet.of( | ||||
|                             DubStatus.Dubbed | ||||
|                         ) else EnumSet.of(DubStatus.Subbed), | ||||
|                         subEpisodes = epNum, | ||||
|                         dubEpisodes = epNum, | ||||
|                     ) | ||||
|                 }) | ||||
|         ) | ||||
| 
 | ||||
|         for (i in urls) { | ||||
|             try { | ||||
|  | @ -72,7 +83,9 @@ class DoramasYTProvider:MainAPI() { | |||
|                         TvType.Anime, | ||||
|                         poster, | ||||
|                         null, | ||||
|                         if (title.contains("Latino") || title.contains("Castellano")) EnumSet.of(DubStatus.Dubbed) else EnumSet.of(DubStatus.Subbed), | ||||
|                         if (title.contains("Latino") || title.contains("Castellano")) EnumSet.of( | ||||
|                             DubStatus.Dubbed | ||||
|                         ) else EnumSet.of(DubStatus.Subbed), | ||||
|                     ) | ||||
|                 } | ||||
| 
 | ||||
|  | @ -87,28 +100,32 @@ class DoramasYTProvider:MainAPI() { | |||
|     } | ||||
| 
 | ||||
|     override suspend fun search(query: String): ArrayList<SearchResponse> { | ||||
|         val search = app.get("$mainUrl/buscar?q=$query", timeout = 120).document.select(".col-6").map { | ||||
|             val title = it.selectFirst(".animedtls p").text() | ||||
|             val href = it.selectFirst("a").attr("href") | ||||
|             val image = it.selectFirst(".animes img").attr("src") | ||||
|             AnimeSearchResponse( | ||||
|                 title, | ||||
|                 href, | ||||
|                 this.name, | ||||
|                 TvType.Anime, | ||||
|                 image, | ||||
|                 null, | ||||
|                 if (title.contains("Latino") || title.contains("Castellano")) EnumSet.of(DubStatus.Dubbed) else EnumSet.of(DubStatus.Subbed), | ||||
|             ) | ||||
|         } | ||||
|         val search = | ||||
|             app.get("$mainUrl/buscar?q=$query", timeout = 120).document.select(".col-6").map { | ||||
|                 val title = it.selectFirst(".animedtls p").text() | ||||
|                 val href = it.selectFirst("a").attr("href") | ||||
|                 val image = it.selectFirst(".animes img").attr("src") | ||||
|                 AnimeSearchResponse( | ||||
|                     title, | ||||
|                     href, | ||||
|                     this.name, | ||||
|                     TvType.Anime, | ||||
|                     image, | ||||
|                     null, | ||||
|                     if (title.contains("Latino") || title.contains("Castellano")) EnumSet.of( | ||||
|                         DubStatus.Dubbed | ||||
|                     ) else EnumSet.of(DubStatus.Subbed), | ||||
|                 ) | ||||
|             } | ||||
|         return ArrayList(search) | ||||
|     } | ||||
| 
 | ||||
|     override suspend fun load(url: String): LoadResponse { | ||||
|         val doc = app.get(url, timeout = 120).document | ||||
|         val poster = doc.selectFirst("div.flimimg img.img1").attr("src") | ||||
|         val title = doc.selectFirst("h1").text() | ||||
|         val type = doc.selectFirst("h4").text() | ||||
|         val description = doc.selectFirst("p.textComplete").text().replace("Ver menos","") | ||||
|         val description = doc.selectFirst("p.textComplete").text().replace("Ver menos", "") | ||||
|         val genres = doc.select(".nobel a").map { it.text() } | ||||
|         val status = when (doc.selectFirst(".state h6")?.text()) { | ||||
|             "Estreno" -> ShowStatus.Ongoing | ||||
|  | @ -129,13 +146,14 @@ class DoramasYTProvider:MainAPI() { | |||
|             tags = genres | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     override suspend fun loadLinks( | ||||
|         data: String, | ||||
|         isCasting: Boolean, | ||||
|         subtitleCallback: (SubtitleFile) -> Unit, | ||||
|         callback: (ExtractorLink) -> Unit | ||||
|     ): Boolean { | ||||
|         app.get(data).document.select("div.playother p").forEach { | ||||
|         app.get(data).document.select("div.playother p").apmap { | ||||
|             val encodedurl = it.select("p").attr("data-player") | ||||
|             val urlDecoded = base64Decode(encodedurl) | ||||
|             val url = (urlDecoded).replace("https://doramasyt.com/reproductor?url=", "") | ||||
|  |  | |||
|  | @ -157,13 +157,10 @@ class DramaSeeProvider : MainAPI() { | |||
|         callback: (ExtractorLink) -> Unit | ||||
|     ): Boolean { | ||||
|         var count = 0 | ||||
|         mapper.readValue<List<String>>(data).forEach { item -> | ||||
|         mapper.readValue<List<String>>(data).apmap { item -> | ||||
|             if (item.isNotEmpty()) { | ||||
|                 count++ | ||||
|                 var url = item.trim() | ||||
|                 if (url.startsWith("//")) { | ||||
|                     url = "https:$url" | ||||
|                 } | ||||
|                 var url = fixUrl(item.trim()) | ||||
|                 //Log.i(this.name, "Result => (url) ${url}") | ||||
|                 when { | ||||
|                     url.startsWith("https://asianembed.io") -> { | ||||
|  |  | |||
|  | @ -69,7 +69,17 @@ class FilmanProvider : MainAPI() { | |||
|                 val img = i.selectFirst("> img").attr("src").replace("/thumb/", "/big/") | ||||
|                 val name = i.selectFirst(".title").text() | ||||
|                 if (type === TvType.TvSeries) { | ||||
|                     returnValue.add(TvSeriesSearchResponse(name, href, this.name, type, img, null, null)) | ||||
|                     returnValue.add( | ||||
|                         TvSeriesSearchResponse( | ||||
|                             name, | ||||
|                             href, | ||||
|                             this.name, | ||||
|                             type, | ||||
|                             img, | ||||
|                             null, | ||||
|                             null | ||||
|                         ) | ||||
|                     ) | ||||
|                 } else { | ||||
|                     returnValue.add(MovieSearchResponse(name, href, this.name, type, img, null)) | ||||
|                 } | ||||
|  | @ -98,15 +108,26 @@ class FilmanProvider : MainAPI() { | |||
|             val regex = Regex("""\[s(\d{1,3})e(\d{1,3})]""").find(e) | ||||
|             if (regex != null) { | ||||
|                 val eid = regex.groups | ||||
|                 episodes.add(TvSeriesEpisode( | ||||
|                     e.split("]")[1].trim(), | ||||
|                     eid[1]?.value?.toInt(), | ||||
|                     eid[2]?.value?.toInt(), | ||||
|                     episode.attr("href"), | ||||
|                 )) | ||||
|                 episodes.add( | ||||
|                     TvSeriesEpisode( | ||||
|                         e.split("]")[1].trim(), | ||||
|                         eid[1]?.value?.toInt(), | ||||
|                         eid[2]?.value?.toInt(), | ||||
|                         episode.attr("href"), | ||||
|                     ) | ||||
|                 ) | ||||
|             } | ||||
|         } | ||||
|         return TvSeriesLoadResponse(title, url, name, TvType.TvSeries, episodes, posterUrl, year, plot) | ||||
|         return TvSeriesLoadResponse( | ||||
|             title, | ||||
|             url, | ||||
|             name, | ||||
|             TvType.TvSeries, | ||||
|             episodes, | ||||
|             posterUrl, | ||||
|             year, | ||||
|             plot | ||||
|         ) | ||||
|     } | ||||
| 
 | ||||
|     override suspend fun loadLinks( | ||||
|  | @ -115,20 +136,16 @@ class FilmanProvider : MainAPI() { | |||
|         subtitleCallback: (SubtitleFile) -> Unit, | ||||
|         callback: (ExtractorLink) -> Unit | ||||
|     ): Boolean { | ||||
|         if(data.isEmpty()) { | ||||
|             return false | ||||
|         } | ||||
|         val document = if (data.startsWith("http")) | ||||
|             Jsoup.parse(app.get(data).text).select("#links").first() | ||||
|             else Jsoup.parse(data) | ||||
|             app.get(data).document.select("#links").first() | ||||
|         else Jsoup.parse(data) | ||||
| 
 | ||||
|         val items = document.select(".link-to-video") | ||||
|         for (i in items) { | ||||
|             val decoded = base64Decode(i.select("a").attr("data-iframe")) | ||||
|         document.select(".link-to-video")?.apmap { item -> | ||||
|             val decoded = base64Decode(item.select("a").attr("data-iframe")) | ||||
|             val link = mapper.readValue<LinkElement>(decoded).src | ||||
|             loadExtractor(link, null, callback) | ||||
|         } | ||||
|        return true | ||||
|         return true | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,8 +1,8 @@ | |||
| package com.lagradost.cloudstream3.movieproviders | ||||
| 
 | ||||
| import com.lagradost.cloudstream3.* | ||||
| import com.lagradost.cloudstream3.extractors.StreamTape | ||||
| import com.lagradost.cloudstream3.utils.ExtractorLink | ||||
| import com.lagradost.cloudstream3.utils.loadExtractor | ||||
| import org.jsoup.Jsoup | ||||
| import java.net.URLEncoder | ||||
| 
 | ||||
|  | @ -18,8 +18,23 @@ class IHaveNoTvProvider : MainAPI() { | |||
|         // Uhh, I am too lazy to scrape the "latest documentaries" and "recommended documentaries", | ||||
|         // so I am just scraping 3 random categories | ||||
|         val allCategories = listOf( | ||||
|             "astronomy", "brain", "creativity", "design", "economics", "environment", "health", "history", | ||||
|             "lifehack", "math", "music", "nature", "people", "physics", "science", "technology", "travel" | ||||
|             "astronomy", | ||||
|             "brain", | ||||
|             "creativity", | ||||
|             "design", | ||||
|             "economics", | ||||
|             "environment", | ||||
|             "health", | ||||
|             "history", | ||||
|             "lifehack", | ||||
|             "math", | ||||
|             "music", | ||||
|             "nature", | ||||
|             "people", | ||||
|             "physics", | ||||
|             "science", | ||||
|             "technology", | ||||
|             "travel" | ||||
|         ) | ||||
| 
 | ||||
|         val categories = allCategories.asSequence().shuffled().take(3) | ||||
|  | @ -82,7 +97,9 @@ class IHaveNoTvProvider : MainAPI() { | |||
|                 res.selectFirst("a[href][title]") | ||||
|             } | ||||
|             val year = | ||||
|                 Regex("""•?\s+(\d{4})\s+•""").find(res.selectFirst(".episodeMeta").text())?.destructured?.component1() | ||||
|                 Regex("""•?\s+(\d{4})\s+•""").find( | ||||
|                     res.selectFirst(".episodeMeta").text() | ||||
|                 )?.destructured?.component1() | ||||
|                     ?.toIntOrNull() | ||||
| 
 | ||||
|             val title = aTag.attr("title") | ||||
|  | @ -138,7 +155,8 @@ class IHaveNoTvProvider : MainAPI() { | |||
|                     ep.selectFirst(".episodeMeta").text() | ||||
|                 )?.destructured?.component1()?.toIntOrNull() | ||||
| 
 | ||||
|                 categories.addAll(ep.select(".episodeMeta > a[href*=\"/category/\"]").map { it.text().trim() }) | ||||
|                 categories.addAll( | ||||
|                     ep.select(".episodeMeta > a[href*=\"/category/\"]").map { it.text().trim() }) | ||||
| 
 | ||||
|                 TvSeriesEpisode( | ||||
|                     epTitle, | ||||
|  | @ -165,7 +183,8 @@ class IHaveNoTvProvider : MainAPI() { | |||
|                 description, | ||||
|                 null, | ||||
|                 null, | ||||
|                 soup.selectFirst(".videoDetails").select("a[href*=\"/category/\"]").map { it.text().trim() } | ||||
|                 soup.selectFirst(".videoDetails").select("a[href*=\"/category/\"]") | ||||
|                     .map { it.text().trim() } | ||||
|             )) | ||||
|         } | ||||
| 
 | ||||
|  | @ -201,9 +220,7 @@ class IHaveNoTvProvider : MainAPI() { | |||
| 
 | ||||
|         val iframe = soup.selectFirst("#videoWrap iframe") | ||||
|         if (iframe != null) { | ||||
|             if (iframe.attr("src").startsWith("https://streamtape.com")) { | ||||
|                 StreamTape().getSafeUrl(iframe.attr("src"))?.forEach(callback) | ||||
|             } | ||||
|             loadExtractor(iframe.attr("src"), null, callback) | ||||
|         } | ||||
|         return true | ||||
|     } | ||||
|  |  | |||
|  | @ -1,6 +1,5 @@ | |||
| package com.lagradost.cloudstream3.movieproviders | ||||
| 
 | ||||
| import android.util.Log | ||||
| import com.fasterxml.jackson.module.kotlin.readValue | ||||
| import com.lagradost.cloudstream3.* | ||||
| import com.lagradost.cloudstream3.extractors.* | ||||
|  | @ -9,7 +8,6 @@ import com.lagradost.cloudstream3.utils.AppUtils.toJson | |||
| import com.lagradost.cloudstream3.utils.ExtractorLink | ||||
| import com.lagradost.cloudstream3.utils.loadExtractor | ||||
| import org.jsoup.Jsoup | ||||
| import kotlin.Exception | ||||
| 
 | ||||
| class KdramaHoodProvider : MainAPI() { | ||||
|     override val mainUrl = "https://kdramahood.com" | ||||
|  | @ -131,13 +129,10 @@ class KdramaHoodProvider : MainAPI() { | |||
|                 if (!epLinksContent.isNullOrEmpty()) { | ||||
|                     //Log.i(this.name, "Result => (epLinksContent) ${Jsoup.parse(epLinksContent)?.select("div")}") | ||||
|                     Jsoup.parse(epLinksContent)?.select("div")?.forEach { em -> | ||||
|                         var href = em?.html()?.trim()?.removePrefix("'") ?: return@forEach | ||||
|                         if (href.startsWith("//")) { | ||||
|                             href = "https:$href" | ||||
|                         } | ||||
|                         val href = em?.html()?.trim()?.removePrefix("'") ?: return@forEach | ||||
|                         //Log.i(this.name, "Result => (ep#$count link) $href") | ||||
|                         if (href.isNotEmpty()) { | ||||
|                             listOfLinks.add(href) | ||||
|                             listOfLinks.add(fixUrl(href)) | ||||
|                         } | ||||
|                     } | ||||
|                     /* Doesn't get all links for some reasons | ||||
|  | @ -188,7 +183,7 @@ class KdramaHoodProvider : MainAPI() { | |||
|         callback: (ExtractorLink) -> Unit | ||||
|     ): Boolean { | ||||
|         var count = 0 | ||||
|         mapper.readValue<List<String>>(data).forEach { item -> | ||||
|         mapper.readValue<List<String>>(data).apmap { item -> | ||||
|             if (item.isNotEmpty()) { | ||||
|                 count++ | ||||
|                 var url = item.trim() | ||||
|  |  | |||
|  | @ -231,7 +231,7 @@ open class PelisplusProviderTemplate : MainAPI() { | |||
|                 null | ||||
|             } | ||||
|         } | ||||
|         servers.forEach { | ||||
|         servers.apmap { | ||||
|             // When checking strings make sure to make them lowercase and trimmed because edgecases like "beta server " wouldn't work otherwise. | ||||
|             if (it.first.trim().equals("beta server", ignoreCase = true)) { | ||||
|                 // Group 1: link, Group 2: Label | ||||
|  |  | |||
|  | @ -1,6 +1,5 @@ | |||
| package com.lagradost.cloudstream3.movieproviders | ||||
| 
 | ||||
| import android.util.Log | ||||
| import com.fasterxml.jackson.module.kotlin.readValue | ||||
| import com.lagradost.cloudstream3.* | ||||
| import com.lagradost.cloudstream3.utils.AppUtils.toJson | ||||
|  | @ -192,7 +191,7 @@ class PinoyHDXyzProvider : MainAPI() { | |||
|         callback: (ExtractorLink) -> Unit | ||||
|     ): Boolean { | ||||
|         var count = 0 | ||||
|         mapper.readValue<List<String>>(data).forEach { item -> | ||||
|         mapper.readValue<List<String>>(data).apmap { item -> | ||||
|             if (item.isNotEmpty()) { | ||||
|                 val url = item.trim() | ||||
|                 loadExtractor(url, mainUrl, callback) | ||||
|  |  | |||
|  | @ -179,7 +179,7 @@ class PinoyMoviePediaProvider : MainAPI() { | |||
|     ): Boolean { | ||||
|         // parse movie servers | ||||
|         var count = 0 | ||||
|         mapper.readValue<List<String>>(data).forEach { link -> | ||||
|         mapper.readValue<List<String>>(data).apmap { link -> | ||||
|             count++ | ||||
|             if (link.contains("fembed.com")) { | ||||
|                 val extractor = FEmbed() | ||||
|  |  | |||
|  | @ -136,7 +136,7 @@ class PinoyMoviesEsProvider : MainAPI() { | |||
|             it?.attr("data-post") ?: return@mapNotNull null | ||||
|         }?.filter { it.isNotEmpty() }?.distinct() ?: listOf() | ||||
| 
 | ||||
|         postlist.forEach { datapost -> | ||||
|         postlist.apmap { datapost -> | ||||
|             //Log.i(this.name, "Result => (datapost) ${datapost}") | ||||
|             val content = mapOf( | ||||
|                 Pair("action", "doo_player_ajax"), | ||||
|  | @ -163,7 +163,7 @@ class PinoyMoviesEsProvider : MainAPI() { | |||
|     ): Boolean { | ||||
|         // parse movie servers | ||||
|         var count = 0 | ||||
|         mapper.readValue<List<String>>(data).forEach { link -> | ||||
|         mapper.readValue<List<String>>(data).apmap { link -> | ||||
|             count++ | ||||
|             //Log.i(this.name, "Result => (link) $link") | ||||
|             if (link.startsWith("https://vstreamhub.com")) { | ||||
|  |  | |||
|  | @ -215,10 +215,9 @@ class SflixProvider(providerUrl: String, providerName: String) : MainAPI() { | |||
|     ): Boolean { | ||||
|         val urls = (tryParseJson<Pair<String, String>>(data)?.let { (prefix, server) -> | ||||
|             val episodesUrl = "$mainUrl/ajax/v2/episode/servers/$server" | ||||
|             val episodes = app.get(episodesUrl).text | ||||
| 
 | ||||
|             // Supported streams, they're identical | ||||
|             Jsoup.parse(episodes).select("a").mapNotNull { element -> | ||||
|             app.get(episodesUrl).document.select("a").mapNotNull { element -> | ||||
|                 val id = element?.attr("data-id") ?: return@mapNotNull null | ||||
|                 if (element.select("span")?.text()?.trim()?.isValidServer() == true) { | ||||
|                     "$prefix.$id".replace("/tv/", "/watch-tv/") | ||||
|  | @ -250,7 +249,6 @@ class SflixProvider(providerUrl: String, providerName: String) : MainAPI() { | |||
|                 mapped.sources2 to "source 3", | ||||
|                 mapped.sourcesBackup to "source backup" | ||||
|             ).forEach { (sources, sourceName) -> | ||||
|                 println("SOURCE:::: $sourceName $sources") | ||||
|                 sources?.forEach { | ||||
|                     it?.toExtractorLink(this, sourceName)?.forEach(callback) | ||||
|                 } | ||||
|  |  | |||
|  | @ -77,36 +77,40 @@ open class VidstreamProviderTemplate : MainAPI() { | |||
|         val description = soup.selectFirst(".post-entry")?.text()?.trim() | ||||
|         var poster: String? = null | ||||
| 
 | ||||
|         val episodes = soup.select(".listing.items.lists > .video-block").withIndex().map { (_, li) -> | ||||
|             val epTitle = if (li.selectFirst(".name") != null) | ||||
|                 if (li.selectFirst(".name").text().contains("Episode")) | ||||
|                     "Episode " + li.selectFirst(".name").text().split("Episode")[1].trim() | ||||
|                 else | ||||
|                     li.selectFirst(".name").text() | ||||
|             else "" | ||||
|             val epThumb = li.selectFirst("img")?.attr("src") | ||||
|             val epDate = li.selectFirst(".meta > .date").text() | ||||
|         val episodes = | ||||
|             soup.select(".listing.items.lists > .video-block").withIndex().map { (_, li) -> | ||||
|                 val epTitle = if (li.selectFirst(".name") != null) | ||||
|                     if (li.selectFirst(".name").text().contains("Episode")) | ||||
|                         "Episode " + li.selectFirst(".name").text().split("Episode")[1].trim() | ||||
|                     else | ||||
|                         li.selectFirst(".name").text() | ||||
|                 else "" | ||||
|                 val epThumb = li.selectFirst("img")?.attr("src") | ||||
|                 val epDate = li.selectFirst(".meta > .date").text() | ||||
| 
 | ||||
|             if (poster == null) { | ||||
|                 poster = li.selectFirst("img")?.attr("onerror")?.split("=")?.get(1)?.replace(Regex("[';]"), "") | ||||
|             } | ||||
|                 if (poster == null) { | ||||
|                     poster = li.selectFirst("img")?.attr("onerror")?.split("=")?.get(1) | ||||
|                         ?.replace(Regex("[';]"), "") | ||||
|                 } | ||||
| 
 | ||||
|             val epNum = Regex("""Episode (\d+)""").find(epTitle)?.destructured?.component1()?.toIntOrNull() | ||||
|                 val epNum = Regex("""Episode (\d+)""").find(epTitle)?.destructured?.component1() | ||||
|                     ?.toIntOrNull() | ||||
| 
 | ||||
|             TvSeriesEpisode( | ||||
|                 epTitle, | ||||
|                 null, | ||||
|                 epNum, | ||||
|                 fixUrl(li.selectFirst("a").attr("href")), | ||||
|                 epThumb, | ||||
|                 epDate | ||||
|             ) | ||||
|         }.reversed() | ||||
|                 TvSeriesEpisode( | ||||
|                     epTitle, | ||||
|                     null, | ||||
|                     epNum, | ||||
|                     fixUrl(li.selectFirst("a").attr("href")), | ||||
|                     epThumb, | ||||
|                     epDate | ||||
|                 ) | ||||
|             }.reversed() | ||||
| 
 | ||||
|         val year = episodes.first().date?.split("-")?.get(0)?.toIntOrNull() | ||||
| 
 | ||||
|         // Make sure to get the type right to display the correct UI. | ||||
|         val tvType = if (episodes.size == 1 && episodes[0].name == title) TvType.Movie else TvType.TvSeries | ||||
|         val tvType = | ||||
|             if (episodes.size == 1 && episodes[0].name == title) TvType.Movie else TvType.TvSeries | ||||
| 
 | ||||
|         return when (tvType) { | ||||
|             TvType.TvSeries -> { | ||||
|  | @ -157,7 +161,8 @@ open class VidstreamProviderTemplate : MainAPI() { | |||
|                 val elements = inner.select(".video-block").map { | ||||
|                     val link = fixUrl(it.select("a").attr("href")) | ||||
|                     val image = it.select(".picture > img").attr("src") | ||||
|                     val name = it.select("div.name").text().trim().replace(Regex("""[Ee]pisode \d+"""), "") | ||||
|                     val name = | ||||
|                         it.select("div.name").text().trim().replace(Regex("""[Ee]pisode \d+"""), "") | ||||
|                     val isSeries = (name.contains("Season") || name.contains("Episode")) | ||||
| 
 | ||||
|                     if (isSeries) { | ||||
|  | @ -188,9 +193,7 @@ open class VidstreamProviderTemplate : MainAPI() { | |||
|                         title, elements | ||||
|                     ) | ||||
|                 ) | ||||
| 
 | ||||
|             } | ||||
| 
 | ||||
|         } | ||||
|         return HomePageResponse(homePageList) | ||||
|     } | ||||
|  | @ -206,7 +209,8 @@ open class VidstreamProviderTemplate : MainAPI() { | |||
|         callback: (ExtractorLink) -> Unit | ||||
|     ): Boolean { | ||||
|         // "?: return" is a very useful statement which returns if the iframe link isn't found. | ||||
|         val iframeLink = Jsoup.parse(app.get(data).text).selectFirst("iframe")?.attr("src") ?: return false | ||||
|         val iframeLink = | ||||
|             Jsoup.parse(app.get(data).text).selectFirst("iframe")?.attr("src") ?: return false | ||||
| 
 | ||||
|         // In this case the video player is a vidstream clone and can be handled by the vidstream extractor. | ||||
|         // This case is a both unorthodox and you normally do not call extractors as they detect the url returned and does the rest. | ||||
|  | @ -228,13 +232,15 @@ open class VidstreamProviderTemplate : MainAPI() { | |||
|                 null | ||||
|             } | ||||
|         } | ||||
|         servers.forEach { | ||||
|         servers.apmap { | ||||
|             // When checking strings make sure to make them lowercase and trimmed because edgecases like "beta server " wouldn't work otherwise. | ||||
|             if (it.first.trim().equals( "beta server", ignoreCase = true)) { | ||||
|             if (it.first.trim().equals("beta server", ignoreCase = true)) { | ||||
|                 // Group 1: link, Group 2: Label | ||||
|                 // Regex can be used to effectively parse small amounts of json without bothering with writing a json class. | ||||
|                 val sourceRegex = Regex("""sources:[\W\w]*?file:\s*["'](.*?)["'][\W\w]*?label:\s*["'](.*?)["']""") | ||||
|                 val trackRegex = Regex("""tracks:[\W\w]*?file:\s*["'](.*?)["'][\W\w]*?label:\s*["'](.*?)["']""") | ||||
|                 val sourceRegex = | ||||
|                     Regex("""sources:[\W\w]*?file:\s*["'](.*?)["'][\W\w]*?label:\s*["'](.*?)["']""") | ||||
|                 val trackRegex = | ||||
|                     Regex("""tracks:[\W\w]*?file:\s*["'](.*?)["'][\W\w]*?label:\s*["'](.*?)["']""") | ||||
| 
 | ||||
|                 // Having a referer is often required. It's a basic security check most providers have. | ||||
|                 // Try to replicate what your browser does. | ||||
|  |  | |||
|  | @ -174,12 +174,9 @@ class WatchAsianProvider : MainAPI() { | |||
|             getServerLinks(data) | ||||
|         } else { data } | ||||
|         var count = 0 | ||||
|         mapper.readValue<List<String>>(links).forEach { item -> | ||||
|         mapper.readValue<List<String>>(links).apmap { item -> | ||||
|             count++ | ||||
|             var url = item.trim() | ||||
|             if (url.startsWith("//")) { | ||||
|                 url = "https:$url" | ||||
|             } | ||||
|             val url = fixUrl(item.trim()) | ||||
|             //Log.i(this.name, "Result => (url) $url") | ||||
|             if (url.startsWith("https://asianembed.io")) { | ||||
|                 // Fetch links | ||||
|  |  | |||
|  | @ -9,7 +9,7 @@ import java.net.InetAddress | |||
|  * Based on https://github.com/tachiyomiorg/tachiyomi/blob/master/app/src/main/java/eu/kanade/tachiyomi/network/DohProviders.kt | ||||
|  */ | ||||
| 
 | ||||
|  fun OkHttpClient.Builder.addGenericDns(url: String, ips: List<String>) = dns( | ||||
| fun OkHttpClient.Builder.addGenericDns(url: String, ips: List<String>) = dns( | ||||
|     DnsOverHttps | ||||
|         .Builder() | ||||
|         .client(build()) | ||||
|  |  | |||
|  | @ -1,6 +1,7 @@ | |||
| package com.lagradost.cloudstream3.network | ||||
| 
 | ||||
| import android.content.Context | ||||
| import android.util.Log | ||||
| import androidx.preference.PreferenceManager | ||||
| import com.fasterxml.jackson.module.kotlin.readValue | ||||
| import com.lagradost.cloudstream3.R | ||||
|  | @ -286,6 +287,7 @@ open class Requests { | |||
|         timeout: Long = 0L, | ||||
|         interceptor: Interceptor? = null, | ||||
|     ): AppResponse { | ||||
|         Log.i("GET", url) | ||||
|         val client = baseClient | ||||
|             .newBuilder() | ||||
|             .followRedirects(allowRedirects) | ||||
|  | @ -315,6 +317,7 @@ open class Requests { | |||
|         cacheUnit: TimeUnit = DEFAULT_TIME_UNIT, | ||||
|         timeout: Long = 0L, | ||||
|     ): AppResponse { | ||||
|         Log.i("POST", url) | ||||
|         val client = baseClient | ||||
|             .newBuilder() | ||||
|             .followRedirects(allowRedirects) | ||||
|  | @ -339,6 +342,7 @@ open class Requests { | |||
|         cacheUnit: TimeUnit = DEFAULT_TIME_UNIT, | ||||
|         timeout: Long = 0L | ||||
|     ): AppResponse { | ||||
|         Log.i("PUT", url) | ||||
|         val client = baseClient | ||||
|             .newBuilder() | ||||
|             .followRedirects(allowRedirects) | ||||
|  |  | |||
|  | @ -22,6 +22,7 @@ import com.google.android.material.button.MaterialButton | |||
| import com.lagradost.cloudstream3.* | ||||
| import com.lagradost.cloudstream3.APIHolder.filterProviderByPreferredMedia | ||||
| import com.lagradost.cloudstream3.APIHolder.getApiFromName | ||||
| import com.lagradost.cloudstream3.APIHolder.getApiSettings | ||||
| import com.lagradost.cloudstream3.mvvm.Resource | ||||
| import com.lagradost.cloudstream3.mvvm.logError | ||||
| import com.lagradost.cloudstream3.mvvm.observe | ||||
|  | @ -93,6 +94,22 @@ class SearchFragment : Fragment() { | |||
|     var selectedSearchTypes = mutableListOf<TvType>() | ||||
|     var selectedApis = mutableSetOf<String>() | ||||
| 
 | ||||
|     fun search(query: String?) { | ||||
|         if (query == null) return | ||||
|         context?.getApiSettings()?.let { settings -> | ||||
|             searchViewModel.searchAndCancel( | ||||
|                 query = query, | ||||
|                 providersActive = selectedApis.filter { name -> | ||||
|                     settings.contains(name) && getApiFromName(name).supportedTypes.any { | ||||
|                         selectedSearchTypes.contains( | ||||
|                             it | ||||
|                         ) | ||||
|                     } | ||||
|                 }.toSet() | ||||
|             ) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     override fun onViewCreated(view: View, savedInstanceState: Bundle?) { | ||||
|         super.onViewCreated(view, savedInstanceState) | ||||
| 
 | ||||
|  | @ -274,6 +291,7 @@ class SearchFragment : Fragment() { | |||
| 
 | ||||
|                     button?.isSelected = buttonContains() | ||||
|                     button?.setOnClickListener { | ||||
|                         val last = selectedSearchTypes.toSet() | ||||
|                         selectedSearchTypes.clear() | ||||
|                         selectedSearchTypes.addAll(validTypes) | ||||
|                         for ((otherButton, _) in pairList) { | ||||
|  | @ -281,6 +299,8 @@ class SearchFragment : Fragment() { | |||
|                         } | ||||
|                         it?.context?.setKey(SEARCH_PREF_TAGS, selectedSearchTypes) | ||||
|                         it?.isSelected = true | ||||
|                         if (last != selectedSearchTypes.toSet()) // if you click the same button again the it does nothing | ||||
|                             search(main_search?.query?.toString()) | ||||
|                     } | ||||
| 
 | ||||
|                     button?.setOnLongClickListener { | ||||
|  | @ -292,6 +312,7 @@ class SearchFragment : Fragment() { | |||
|                             selectedSearchTypes.removeAll(validTypes) | ||||
|                         } | ||||
|                         it?.context?.setKey(SEARCH_PREF_TAGS, selectedSearchTypes) | ||||
|                         search(main_search?.query?.toString()) | ||||
|                         return@setOnLongClickListener true | ||||
|                     } | ||||
|                 } | ||||
|  | @ -305,12 +326,7 @@ class SearchFragment : Fragment() { | |||
| 
 | ||||
|         main_search.setOnQueryTextListener(object : SearchView.OnQueryTextListener { | ||||
|             override fun onQueryTextSubmit(query: String): Boolean { | ||||
|                 searchViewModel.searchAndCancel( | ||||
|                     query = query, | ||||
|                     providersActive = selectedApis.filter { name -> | ||||
|                         getApiFromName(name).supportedTypes.any { selectedSearchTypes.contains(it) } | ||||
|                     }.toSet() | ||||
|                 ) | ||||
|                 search(query) | ||||
| 
 | ||||
|                 main_search?.let { | ||||
|                     hideKeyboard(it) | ||||
|  |  | |||
|  | @ -7,6 +7,7 @@ import com.fasterxml.jackson.databind.json.JsonMapper | |||
| import com.fasterxml.jackson.module.kotlin.KotlinModule | ||||
| 
 | ||||
| const val DOWNLOAD_HEADER_CACHE = "download_header_cache" | ||||
| 
 | ||||
| //const val WATCH_HEADER_CACHE = "watch_header_cache" | ||||
| const val DOWNLOAD_EPISODE_CACHE = "download_episode_cache" | ||||
| const val VIDEO_PLAYER_BRIGHTNESS = "video_player_alpha_key" | ||||
|  |  | |||
|  | @ -79,7 +79,7 @@ fun getAndUnpack(string: String): String { | |||
| /** | ||||
|  * Tries to load the appropriate extractor based on link, returns true if any extractor is loaded. | ||||
|  * */ | ||||
| suspend fun loadExtractor(url: String, referer: String?, callback: (ExtractorLink) -> Unit) : Boolean { | ||||
| suspend fun loadExtractor(url: String, referer: String? = null, callback: (ExtractorLink) -> Unit) : Boolean { | ||||
|     for (extractor in extractorApis) { | ||||
|         if (url.startsWith(extractor.mainUrl)) { | ||||
|             extractor.getSafeUrl(url, referer)?.forEach(callback) | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue