forked from recloudstream/cloudstream
		
	api refactor, breaking change for forks
This commit is contained in:
		
							parent
							
								
									99fa40eaeb
								
							
						
					
					
						commit
						750b1878cb
					
				
					 49 changed files with 564 additions and 376 deletions
				
			
		|  | @ -36,7 +36,7 @@ android { | |||
|         targetSdkVersion 30 | ||||
| 
 | ||||
|         versionCode 45 | ||||
|         versionName "2.9.17" | ||||
|         versionName "2.9.18" | ||||
| 
 | ||||
|         resValue "string", "app_version", | ||||
|                 "${defaultConfig.versionName}${versionNameSuffix ?: ""}" | ||||
|  |  | |||
|  | @ -13,11 +13,14 @@ import com.fasterxml.jackson.module.kotlin.KotlinModule | |||
| import com.lagradost.cloudstream3.animeproviders.* | ||||
| import com.lagradost.cloudstream3.metaproviders.CrossTmdbProvider | ||||
| import com.lagradost.cloudstream3.movieproviders.* | ||||
| import com.lagradost.cloudstream3.mvvm.logError | ||||
| import com.lagradost.cloudstream3.syncproviders.OAuth2API.Companion.aniListApi | ||||
| import com.lagradost.cloudstream3.syncproviders.OAuth2API.Companion.malApi | ||||
| import com.lagradost.cloudstream3.ui.player.SubtitleData | ||||
| import com.lagradost.cloudstream3.utils.AppUtils.toJson | ||||
| import com.lagradost.cloudstream3.utils.ExtractorLink | ||||
| import okhttp3.Interceptor | ||||
| import java.text.SimpleDateFormat | ||||
| import java.util.* | ||||
| import kotlin.math.absoluteValue | ||||
| 
 | ||||
|  | @ -800,16 +803,6 @@ fun TvType?.isEpisodeBased(): Boolean { | |||
|     return (this == TvType.TvSeries || this == TvType.Anime) | ||||
| } | ||||
| 
 | ||||
| data class AnimeEpisode( | ||||
|     val url: String, | ||||
|     var name: String? = null, | ||||
|     var posterUrl: String? = null, | ||||
|     var date: String? = null, | ||||
|     var rating: Int? = null, | ||||
|     var description: String? = null, | ||||
|     var episode: Int? = null, | ||||
| ) | ||||
| 
 | ||||
| data class TorrentLoadResponse( | ||||
|     override var name: String, | ||||
|     override var url: String, | ||||
|  | @ -841,7 +834,7 @@ data class AnimeLoadResponse( | |||
|     override var posterUrl: String? = null, | ||||
|     override var year: Int? = null, | ||||
| 
 | ||||
|     var episodes: MutableMap<DubStatus, List<AnimeEpisode>> = mutableMapOf(), | ||||
|     var episodes: MutableMap<DubStatus, List<Episode>> = mutableMapOf(), | ||||
|     var showStatus: ShowStatus? = null, | ||||
| 
 | ||||
|     override var plot: String? = null, | ||||
|  | @ -857,7 +850,7 @@ data class AnimeLoadResponse( | |||
|     override var syncData: MutableMap<String, String> = mutableMapOf(), | ||||
| ) : LoadResponse | ||||
| 
 | ||||
| fun AnimeLoadResponse.addEpisodes(status: DubStatus, episodes: List<AnimeEpisode>?) { | ||||
| fun AnimeLoadResponse.addEpisodes(status: DubStatus, episodes: List<Episode>?) { | ||||
|     if (episodes == null) return | ||||
|     this.episodes[status] = episodes | ||||
| } | ||||
|  | @ -912,6 +905,26 @@ data class MovieLoadResponse( | |||
|     override var syncData: MutableMap<String, String> = mutableMapOf(), | ||||
| ) : LoadResponse | ||||
| 
 | ||||
| fun <T> MainAPI.newMovieLoadResponse( | ||||
|     name: String, | ||||
|     url: String, | ||||
|     type: TvType, | ||||
|     data: T?, | ||||
|     initializer: MovieLoadResponse.() -> Unit = { } | ||||
| ): MovieLoadResponse { | ||||
|     val dataUrl = data?.toJson() ?: "" | ||||
|     val builder = MovieLoadResponse( | ||||
|         name = name, | ||||
|         url = url, | ||||
|         apiName = this.name, | ||||
|         type = type, | ||||
|         dataUrl = dataUrl, | ||||
|         comingSoon = dataUrl.isBlank() | ||||
|     ) | ||||
|     builder.initializer() | ||||
|     return builder | ||||
| } | ||||
| 
 | ||||
| fun MainAPI.newMovieLoadResponse( | ||||
|     name: String, | ||||
|     url: String, | ||||
|  | @ -931,23 +944,58 @@ fun MainAPI.newMovieLoadResponse( | |||
|     return builder | ||||
| } | ||||
| 
 | ||||
| data class TvSeriesEpisode( | ||||
|     val name: String? = null, | ||||
|     val season: Int? = null, | ||||
|     val episode: Int? = null, | ||||
|     val data: String, | ||||
|     val posterUrl: String? = null, | ||||
|     val date: String? = null, | ||||
|     val rating: Int? = null, | ||||
|     val description: String? = null, | ||||
| data class Episode( | ||||
|     var data: String, | ||||
|     var name: String? = null, | ||||
|     var season: Int? = null, | ||||
|     var episode: Int? = null, | ||||
|     var posterUrl: String? = null, | ||||
|     var rating: Int? = null, | ||||
|     var description: String? = null, | ||||
|     var date: Long? = null, | ||||
| ) | ||||
| 
 | ||||
| fun Episode.addDate(date: String?, fomat: String = "yyyy-MM-dd") { | ||||
|     try { | ||||
|         this.date = SimpleDateFormat(fomat)?.parse(date ?: return)?.time | ||||
|     } catch (e: Exception) { | ||||
|         logError(e) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fun Episode.addDate(date: Date?) { | ||||
|     this.date = date?.time | ||||
| } | ||||
| 
 | ||||
| fun <T> MainAPI.newEpisode( | ||||
|     url: String, | ||||
|     initializer: Episode.() -> Unit = { }, | ||||
|     fix: Boolean = true, | ||||
| ): Episode { | ||||
|     val builder = Episode( | ||||
|         data = if (fix) fixUrl(url) else url | ||||
|     ) | ||||
|     builder.initializer() | ||||
|     return builder | ||||
| } | ||||
| 
 | ||||
| fun <T> MainAPI.newEpisode( | ||||
|     data: T, | ||||
|     initializer: Episode.() -> Unit = { } | ||||
| ): Episode { | ||||
|     val builder = Episode( | ||||
|         data = data?.toJson() ?: throw ErrorLoadingException("invalid newEpisode") | ||||
|     ) | ||||
|     builder.initializer() | ||||
|     return builder | ||||
| } | ||||
| 
 | ||||
| data class TvSeriesLoadResponse( | ||||
|     override var name: String, | ||||
|     override var url: String, | ||||
|     override var apiName: String, | ||||
|     override var type: TvType, | ||||
|     var episodes: List<TvSeriesEpisode>, | ||||
|     var episodes: List<Episode>, | ||||
| 
 | ||||
|     override var posterUrl: String? = null, | ||||
|     override var year: Int? = null, | ||||
|  | @ -968,7 +1016,7 @@ fun MainAPI.newTvSeriesLoadResponse( | |||
|     name: String, | ||||
|     url: String, | ||||
|     type: TvType, | ||||
|     episodes: List<TvSeriesEpisode>, | ||||
|     episodes: List<Episode>, | ||||
|     initializer: TvSeriesLoadResponse.() -> Unit = { } | ||||
| ): TvSeriesLoadResponse { | ||||
|     val builder = TvSeriesLoadResponse( | ||||
|  |  | |||
|  | @ -232,11 +232,11 @@ class AllAnimeProvider : MainAPI() { | |||
|         val episodes = showData.availableEpisodes.let { | ||||
|             if (it == null) return@let Pair(null, null) | ||||
|             Pair(if (it.sub != 0) ((1..it.sub).map { epNum -> | ||||
|                 AnimeEpisode( | ||||
|                 Episode( | ||||
|                     "$mainUrl/anime/${showData.Id}/episodes/sub/$epNum", episode = epNum | ||||
|                 ) | ||||
|             }) else null, if (it.dub != 0) ((1..it.dub).map { epNum -> | ||||
|                 AnimeEpisode( | ||||
|                 Episode( | ||||
|                     "$mainUrl/anime/${showData.Id}/episodes/dub/$epNum", episode = epNum | ||||
|                 ) | ||||
|             }) else null) | ||||
|  |  | |||
|  | @ -65,7 +65,7 @@ class AnimeFlickProvider : MainAPI() { | |||
|         val episodes = doc.select("#collapseOne .block-space > .row > div:nth-child(2)").map { | ||||
|             val name = it.selectFirst("a").text() | ||||
|             val link = mainUrl + it.selectFirst("a").attr("href") | ||||
|             AnimeEpisode(link, name) | ||||
|             Episode(link, name) | ||||
|         }.reversed() | ||||
| 
 | ||||
|         return newAnimeLoadResponse(title, url, getType(title)) { | ||||
|  |  | |||
|  | @ -189,7 +189,7 @@ class AnimePaheProvider : MainAPI() { | |||
|     ) | ||||
| 
 | ||||
| 
 | ||||
|     private suspend fun generateListOfEpisodes(link: String): ArrayList<AnimeEpisode> { | ||||
|     private suspend fun generateListOfEpisodes(link: String): ArrayList<Episode> { | ||||
|         try { | ||||
|             val attrs = link.split('/') | ||||
|             val id = attrs[attrs.size - 1].split("?")[0] | ||||
|  | @ -204,7 +204,7 @@ class AnimePaheProvider : MainAPI() { | |||
|             val perPage = data.perPage | ||||
|             val total = data.total | ||||
|             var ep = 1 | ||||
|             val episodes = ArrayList<AnimeEpisode>() | ||||
|             val episodes = ArrayList<Episode>() | ||||
| 
 | ||||
|             fun getEpisodeTitle(k: AnimeData): String { | ||||
|                 return k.title.ifEmpty { | ||||
|  | @ -215,14 +215,11 @@ class AnimePaheProvider : MainAPI() { | |||
|             if (lastPage == 1 && perPage > total) { | ||||
|                 data.data.forEach { | ||||
|                     episodes.add( | ||||
|                         AnimeEpisode( | ||||
|                             "$mainUrl/api?m=links&id=${it.animeId}&session=${it.session}&p=kwik!!TRUE!!", | ||||
|                             getEpisodeTitle(it), | ||||
|                             it.snapshot.ifEmpty { | ||||
|                                 null | ||||
|                             }, | ||||
|                             it.createdAt | ||||
|                         ) | ||||
|                         newEpisode("$mainUrl/api?m=links&id=${it.animeId}&session=${it.session}&p=kwik!!TRUE!!") { | ||||
|                             addDate(it.createdAt) | ||||
|                             this.name = getEpisodeTitle(it) | ||||
|                             this.posterUrl = it.snapshot | ||||
|                         } | ||||
|                     ) | ||||
|                 } | ||||
|             } else { | ||||
|  | @ -230,7 +227,7 @@ class AnimePaheProvider : MainAPI() { | |||
|                     for (i in 0 until perPage) { | ||||
|                         if (ep <= total) { | ||||
|                             episodes.add( | ||||
|                                 AnimeEpisode( | ||||
|                                 Episode( | ||||
|                                     "$mainUrl/api?m=release&id=${id}&sort=episode_asc&page=${page + 1}&ep=${ep}!!FALSE!!" | ||||
|                                 ) | ||||
|                             ) | ||||
|  |  | |||
|  | @ -152,7 +152,7 @@ class AnimeWorldProvider : MainAPI() { | |||
|         val episodes = servers.select(".server[data-name=\"9\"] .episode").map { | ||||
|             val id = it.select("a").attr("data-id") | ||||
|             val number = it.select("a").attr("data-episode-num").toIntOrNull() | ||||
|             AnimeEpisode( | ||||
|             Episode( | ||||
|                 fixUrl("$mainUrl/api/episode/info?id=$id"), | ||||
|                 episode = number | ||||
|             ) | ||||
|  |  | |||
|  | @ -115,7 +115,7 @@ class AnimeflvnetProvider:MainAPI() { | |||
| 
 | ||||
|     override suspend fun load(url: String): LoadResponse { | ||||
|         val doc = app.get(url).document | ||||
|         val episodes = ArrayList<AnimeEpisode>() | ||||
|         val episodes = ArrayList<Episode>() | ||||
|         val title = doc.selectFirst("h1.Title").text() | ||||
|         val poster = doc.selectFirst("div.AnimeCover div.Image figure img").attr("src") | ||||
|         val description = doc.selectFirst("div.Description p").text() | ||||
|  | @ -137,7 +137,7 @@ class AnimeflvnetProvider:MainAPI() { | |||
|                     val animeid = doc.selectFirst("div.Strs.RateIt").attr("data-id") | ||||
|                     val epthumb = "https://cdn.animeflv.net/screenshots/$animeid/$epNum/th_3.jpg" | ||||
|                     val link = url.replace("/anime/","/ver/")+"-$epNum" | ||||
|                     episodes.add( AnimeEpisode( | ||||
|                     episodes.add( Episode( | ||||
|                         link, | ||||
|                         null, | ||||
|                         posterUrl = epthumb, | ||||
|  |  | |||
|  | @ -106,7 +106,7 @@ class AnimekisaProvider : MainAPI() { | |||
|         ) ShowStatus.Ongoing else ShowStatus.Completed | ||||
|         val episodes = doc.select("div.tab-content ul li.nav-item").map { | ||||
|             val link = it.selectFirst("a").attr("href") | ||||
|             AnimeEpisode(link) | ||||
|             Episode(link) | ||||
|         } | ||||
|         val type = if (doc.selectFirst(".dp-i-stats").toString() | ||||
|                 .contains("Movies") | ||||
|  |  | |||
|  | @ -112,7 +112,7 @@ class DubbedAnimeProvider : MainAPI() { | |||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     private suspend fun getAnimeEpisode(slug: String, isMovie: Boolean): EpisodeInfo { | ||||
|     private suspend fun getEpisode(slug: String, isMovie: Boolean): EpisodeInfo { | ||||
|         val url = | ||||
|             mainUrl + (if (isMovie) "/movies/jsonMovie" else "/xz/v3/jsonEpi") + ".php?slug=$slug&_=$unixTime" | ||||
|         val response = app.get(url).text | ||||
|  | @ -196,7 +196,7 @@ class DubbedAnimeProvider : MainAPI() { | |||
|     ): Boolean { | ||||
|         val serversHTML = (if (data.startsWith(mainUrl)) { // CLASSIC EPISODE | ||||
|             val slug = getSlug(data) | ||||
|             getAnimeEpisode(slug, false).serversHTML | ||||
|             getEpisode(slug, false).serversHTML | ||||
|         } else data).replace("\\", "") | ||||
| 
 | ||||
|         val hls = ArrayList("hl=\"(.*?)\"".toRegex().findAll(serversHTML).map { | ||||
|  | @ -228,7 +228,7 @@ class DubbedAnimeProvider : MainAPI() { | |||
|     override suspend fun load(url: String): LoadResponse { | ||||
|         if (getIsMovie(url)) { | ||||
|             val realSlug = url.replace("movies/", "") | ||||
|             val episode = getAnimeEpisode(realSlug, true) | ||||
|             val episode = getEpisode(realSlug, true) | ||||
|             val poster = episode.previewImg ?: episode.wideImg | ||||
|             return MovieLoadResponse( | ||||
|                 episode.title, | ||||
|  | @ -253,7 +253,7 @@ class DubbedAnimeProvider : MainAPI() { | |||
| 
 | ||||
|             val episodes = document.select("a.epibloks").map { | ||||
|                 val epTitle = it.selectFirst("> div.inwel > span.isgrxx")?.text() | ||||
|                 AnimeEpisode(fixUrl(it.attr("href")), epTitle) | ||||
|                 Episode(fixUrl(it.attr("href")), epTitle) | ||||
|             } | ||||
| 
 | ||||
|             val img = fixUrl(document.select("div.fkimgs > img").attr("src")) | ||||
|  |  | |||
|  | @ -303,7 +303,7 @@ class GogoanimeProvider : MainAPI() { | |||
|         val params = mapOf("ep_start" to "0", "ep_end" to "2000", "id" to animeId) | ||||
| 
 | ||||
|         val episodes = app.get(episodeloadApi, params = params).document.select("a").map { | ||||
|             AnimeEpisode( | ||||
|             Episode( | ||||
|                 fixUrl(it.attr("href").trim()), | ||||
|                 "Episode " + it.selectFirst(".name").text().replace("EP", "").trim() | ||||
|             ) | ||||
|  |  | |||
|  | @ -94,7 +94,7 @@ class KawaiifuProvider : MainAPI() { | |||
|         val episodes = Jsoup.parse( | ||||
|             app.get(episodesLink).text | ||||
|         ).selectFirst(".list-ep").select("li").map { | ||||
|             AnimeEpisode( | ||||
|             Episode( | ||||
|                 it.selectFirst("a").attr("href"), | ||||
|                 if (it.text().trim().toIntOrNull() != null) "Episode ${it.text().trim()}" else it.text().trim() | ||||
|             ) | ||||
|  |  | |||
|  | @ -5,7 +5,6 @@ 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 MonoschinosProvider : MainAPI() { | ||||
|  | @ -132,7 +131,7 @@ class MonoschinosProvider : MainAPI() { | |||
|             val name = it.selectFirst("p.animetitles").text() | ||||
|             val link = it.selectFirst("a").attr("href") | ||||
|             val epThumb = it.selectFirst(".animeimghv").attr("data-src") | ||||
|             AnimeEpisode(link, name, posterUrl = epThumb) | ||||
|             Episode(link, name, posterUrl = epThumb) | ||||
|         } | ||||
|         return newAnimeLoadResponse(title, url, getType(type)) { | ||||
|             posterUrl = poster | ||||
|  |  | |||
|  | @ -203,7 +203,7 @@ class NineAnimeProvider : MainAPI() { | |||
|         )?.select("ul.episodes li a")?.mapNotNull { | ||||
|             val link = it?.attr("href") ?: return@mapNotNull null | ||||
|             val name = "Episode ${it.text()}" | ||||
|             AnimeEpisode(link, name) | ||||
|             Episode(link, name) | ||||
|         } ?: return null | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -125,16 +125,15 @@ class TenshiProvider : MainAPI() { | |||
|     } | ||||
| 
 | ||||
|     @SuppressLint("SimpleDateFormat") | ||||
|     private fun dateParser(dateString: String?): String? { | ||||
|     private fun dateParser(dateString: String?): Date? { | ||||
|         if (dateString == null) return null | ||||
|         try { | ||||
|             val format = SimpleDateFormat("dd 'of' MMM',' yyyy") | ||||
|             val newFormat = SimpleDateFormat("dd-MM-yyyy") | ||||
|             val data = format.parse( | ||||
|                 dateString.replace("th ", " ").replace("st ", " ").replace("nd ", " ") | ||||
|                     .replace("rd ", " ") | ||||
|             ) ?: return null | ||||
|             return newFormat.format(data) | ||||
|             return data | ||||
|         } catch (e: Exception) { | ||||
|             return null | ||||
|         } | ||||
|  | @ -246,14 +245,12 @@ class TenshiProvider : MainAPI() { | |||
| 
 | ||||
|         val episodes = ArrayList(episodeNodes.map { | ||||
|             val title = it.selectFirst(".episode-title")?.text()?.trim() | ||||
|             AnimeEpisode( | ||||
|                 it.attr("href"), | ||||
|                 if(title == "No Title") null else title, | ||||
|                 it.selectFirst("img")?.attr("src"), | ||||
|                 dateParser(it?.selectFirst(".episode-date")?.text()?.trim()), | ||||
|                 null, | ||||
|                 it.attr("data-content").trim(), | ||||
|             ) | ||||
|             newEpisode(it.attr("href")) { | ||||
|                 this.name = if (title == "No Title") null else title | ||||
|                 this.posterUrl = it.selectFirst("img")?.attr("src") | ||||
|                 addDate(dateParser(it?.selectFirst(".episode-date")?.text()?.trim())) | ||||
|                 this.description = it.attr("data-content").trim() | ||||
|             } | ||||
|         }) | ||||
| 
 | ||||
|         val similarAnime = document.select("ul.anime-loop > li > a")?.mapNotNull { element -> | ||||
|  |  | |||
|  | @ -113,28 +113,26 @@ class WatchCartoonOnlineProvider : MainAPI() { | |||
|                 val href = it.attr("href") | ||||
|                 if (match != null) { | ||||
|                     val last = match.groupValues[3] | ||||
|                     return@map TvSeriesEpisode( | ||||
|                     return@map Episode( | ||||
|                         href, | ||||
|                         if (last.startsWith("English")) null else last, | ||||
|                         match.groupValues[1].toIntOrNull(), | ||||
|                         match.groupValues[2].toIntOrNull(), | ||||
|                         href | ||||
|                     ) | ||||
|                 } | ||||
|                 val match2 = Regex("Episode ([0-9]*).*? (.*)").find(text) | ||||
|                 if (match2 != null) { | ||||
|                     val last = match2.groupValues[2] | ||||
|                     return@map TvSeriesEpisode( | ||||
|                     return@map Episode( | ||||
|                         href, | ||||
|                         if (last.startsWith("English")) null else last, | ||||
|                         null, | ||||
|                         match2.groupValues[1].toIntOrNull(), | ||||
|                         href | ||||
|                     ) | ||||
|                 } | ||||
|                 return@map TvSeriesEpisode( | ||||
|                     text, | ||||
|                     null, | ||||
|                     null, | ||||
|                     href | ||||
|                 return@map Episode( | ||||
|                     href, | ||||
|                     text | ||||
|                 ) | ||||
|             } | ||||
|             TvSeriesLoadResponse( | ||||
|  | @ -162,7 +160,7 @@ class WatchCartoonOnlineProvider : MainAPI() { | |||
|                 url, | ||||
|                 this.name, | ||||
|                 TvType.TvSeries, | ||||
|                 listOf(TvSeriesEpisode(title, null, null, url)), | ||||
|                 listOf(Episode(url,title)), | ||||
|                 null, | ||||
|                 null, | ||||
|                 description, | ||||
|  |  | |||
|  | @ -181,7 +181,7 @@ class WcoProvider : MainAPI() { | |||
|         val episodeNodes = document.select(".tab-content .nav-item > a") | ||||
| 
 | ||||
|         val episodes = ArrayList(episodeNodes?.map { | ||||
|             AnimeEpisode(it.attr("href")) | ||||
|             Episode(it.attr("href")) | ||||
|         } ?: ArrayList()) | ||||
| 
 | ||||
|         val statusElem = | ||||
|  |  | |||
|  | @ -231,16 +231,10 @@ class ZoroProvider : MainAPI() { | |||
|                 ).text | ||||
|             ).html | ||||
|         ).select(".ss-list > a[href].ssl-item.ep-item").map { | ||||
|             val name = it?.attr("title") | ||||
|             AnimeEpisode( | ||||
|                 fixUrl(it.attr("href")), | ||||
|                 name, | ||||
|                 null, | ||||
|                 null, | ||||
|                 null, | ||||
|                 null, | ||||
|                 it.selectFirst(".ssli-order")?.text()?.toIntOrNull() | ||||
|             ) | ||||
|             newEpisode(it.attr("href")) { | ||||
|                 this.name = it?.attr("title") | ||||
|                 this.episode = it.selectFirst(".ssli-order")?.text()?.toIntOrNull() | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         val actors = document.select("div.block-actors-content > div.bac-list-wrap > div.bac-item") | ||||
|  |  | |||
|  | @ -94,23 +94,23 @@ open class TmdbProvider : MainAPI() { | |||
|         val episodes = this.seasons?.filter { !disableSeasonZero || (it.season_number ?: 0) != 0 } | ||||
|             ?.mapNotNull { season -> | ||||
|                 season.episodes?.map { episode -> | ||||
|                     TvSeriesEpisode( | ||||
|                         episode.name, | ||||
|                         episode.season_number, | ||||
|                         episode.episode_number, | ||||
|                     Episode( | ||||
|                         TmdbLink( | ||||
|                             episode.external_ids?.imdb_id ?: this.external_ids?.imdb_id, | ||||
|                             this.id, | ||||
|                             episode.episode_number, | ||||
|                             episode.season_number, | ||||
|                         ).toJson(), | ||||
|                         episode.name, | ||||
|                         episode.season_number, | ||||
|                         episode.episode_number, | ||||
|                         getImageUrl(episode.still_path), | ||||
|                         episode.air_date?.toString(), | ||||
|                         episode.rating, | ||||
|                         episode.overview, | ||||
|                         episode.air_date?.time, | ||||
|                     ) | ||||
|                 } ?: (1..(season.episode_count ?: 1)).map { episodeNum -> | ||||
|                     TvSeriesEpisode( | ||||
|                     Episode( | ||||
|                         episode = episodeNum, | ||||
|                         data = TmdbLink( | ||||
|                             this.external_ids?.imdb_id, | ||||
|  |  | |||
|  | @ -63,20 +63,18 @@ class AkwamProvider : MainAPI() { | |||
|         return Regex("""\d+""").find(this)?.groupValues?.firstOrNull()?.toIntOrNull() | ||||
|     } | ||||
| 
 | ||||
|     private fun Element.toTvSeriesEpisode(): TvSeriesEpisode { | ||||
|     private fun Element.toEpisode(): Episode { | ||||
|         val a = select("a.text-white") | ||||
|         val url = a.attr("href") | ||||
|         val title = a.text() | ||||
|         val thumbUrl = select("picture > img").attr("src") | ||||
|         val date = select("p.entry-date").text() | ||||
|         return TvSeriesEpisode( | ||||
|             title, | ||||
|             null, | ||||
|             title.getIntFromText(), | ||||
|             url, | ||||
|             thumbUrl, | ||||
|             date | ||||
|         ) | ||||
|         return newEpisode(url) { | ||||
|             name = title | ||||
|             episode = title.getIntFromText() | ||||
|             posterUrl = thumbUrl | ||||
|             addDate(date) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|  | @ -142,7 +140,7 @@ class AkwamProvider : MainAPI() { | |||
|             } | ||||
|         } else { | ||||
|             val episodes = doc.select("div.bg-primary2.p-4.col-lg-4.col-md-6.col-12").map { | ||||
|                 it.toTvSeriesEpisode() | ||||
|                 it.toEpisode() | ||||
|             }.let { | ||||
|                 val isReversed = it.lastOrNull()?.episode ?: 1 < it.firstOrNull()?.episode ?: 0 | ||||
|                 if (isReversed) | ||||
|  |  | |||
|  | @ -130,7 +130,7 @@ class AllMoviesForYouProvider : MainAPI() { | |||
|             } | ||||
|             if (list.isEmpty()) throw ErrorLoadingException("No Seasons Found") | ||||
| 
 | ||||
|             val episodeList = ArrayList<TvSeriesEpisode>() | ||||
|             val episodeList = ArrayList<Episode>() | ||||
| 
 | ||||
|             for (season in list) { | ||||
|                 val seasonResponse = app.get(season.second).text | ||||
|  | @ -144,15 +144,15 @@ class AllMoviesForYouProvider : MainAPI() { | |||
|                         val name = aName.text() | ||||
|                         val href = aName.attr("href") | ||||
|                         val date = episode.selectFirst("> td.MvTbTtl > span")?.text() | ||||
| 
 | ||||
|                         episodeList.add( | ||||
|                             TvSeriesEpisode( | ||||
|                                 name, | ||||
|                                 season.first, | ||||
|                                 epNum, | ||||
|                                 fixUrl(href), | ||||
|                                 fixUrlNull(poster), | ||||
|                                 date | ||||
|                             ) | ||||
|                             newEpisode(href) { | ||||
|                                 this.name = name | ||||
|                                 this.season = season.first | ||||
|                                 this.episode = epNum | ||||
|                                 this.posterUrl = fixUrlNull(poster) | ||||
|                                 addDate(date) | ||||
|                             } | ||||
|                         ) | ||||
|                     } | ||||
|                 } | ||||
|  | @ -197,7 +197,7 @@ class AllMoviesForYouProvider : MainAPI() { | |||
|             if (id.contains("trembed")) { | ||||
|                 val soup = app.get(id).document | ||||
|                 soup.select("body iframe").map { | ||||
|                     val link = fixUrl(it.attr("src").replace("streamhub.to/d/","streamhub.to/e/")) | ||||
|                     val link = fixUrl(it.attr("src").replace("streamhub.to/d/", "streamhub.to/e/")) | ||||
|                     loadExtractor(link, data, callback) | ||||
|                 } | ||||
|             } else loadExtractor(id, data, callback) | ||||
|  |  | |||
|  | @ -83,14 +83,13 @@ class AsiaFlixProvider : MainAPI() { | |||
|         ) | ||||
|     } | ||||
| 
 | ||||
|     private fun Episodes.toTvSeriesEpisode(): TvSeriesEpisode? { | ||||
|     private fun Episodes.toEpisode(): Episode? { | ||||
|         if (videoUrl != null && videoUrl.contains("watch/null") || number == null) return null | ||||
|         return videoUrl?.let { | ||||
|             TvSeriesEpisode( | ||||
|                 null, | ||||
|             Episode( | ||||
|                 it, | ||||
|                 null, | ||||
|                 number, | ||||
|                 it | ||||
|             ) | ||||
|         } | ||||
|     } | ||||
|  | @ -101,7 +100,7 @@ class AsiaFlixProvider : MainAPI() { | |||
|             "$mainUrl$dramaUrl/$_id".replace("drama-detail", "show-details"), | ||||
|             this@AsiaFlixProvider.name, | ||||
|             TvType.AsianDrama, | ||||
|             episodes.mapNotNull { it.toTvSeriesEpisode() }.sortedBy { it.episode }, | ||||
|             episodes.mapNotNull { it.toEpisode() }.sortedBy { it.episode }, | ||||
|             image, | ||||
|             releaseYear, | ||||
|             synopsis, | ||||
|  | @ -115,7 +114,8 @@ class AsiaFlixProvider : MainAPI() { | |||
|         val headers = mapOf("X-Requested-By" to "asiaflix-web") | ||||
|         val response = app.get("$apiUrl/dashboard", headers = headers).text | ||||
| 
 | ||||
|         val customMapper = mapper.copy().configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true) | ||||
|         val customMapper = | ||||
|             mapper.copy().configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true) | ||||
|         // Hack, because it can either be object or a list | ||||
|         val cleanedResponse = Regex(""""data":(\{.*?),\{"sectionName"""").replace(response) { | ||||
|             """"data":null},{"sectionName"""" | ||||
|  | @ -145,14 +145,18 @@ class AsiaFlixProvider : MainAPI() { | |||
|     ): Boolean { | ||||
|         if (isCasting) return false | ||||
|         val headers = mapOf("X-Requested-By" to "asiaflix-web") | ||||
|         app.get("$apiUrl/utility/get-stream-links?url=$data", headers = headers).text.toKotlinObject<Link>().url?.let { | ||||
|         app.get( | ||||
|             "$apiUrl/utility/get-stream-links?url=$data", | ||||
|             headers = headers | ||||
|         ).text.toKotlinObject<Link>().url?.let { | ||||
| //            val fixedUrl = "https://api.asiaflix.app/api/v2/utility/cors-proxy/playlist/${URLEncoder.encode(it, StandardCharsets.UTF_8.toString())}" | ||||
|             callback.invoke( | ||||
|                 ExtractorLink( | ||||
|                     name, | ||||
|                     name, | ||||
|                     it, | ||||
|                     "https://asianload1.com/", /** <------ This provider should be added instead */ | ||||
|                     "https://asianload1.com/", | ||||
|                     /** <------ This provider should be added instead */ | ||||
|                     getQualityFromName(it), | ||||
|                     URI(it).path.endsWith(".m3u8") | ||||
|                 ) | ||||
|  |  | |||
|  | @ -242,11 +242,11 @@ open class BflixProvider() : MainAPI() { | |||
|             val eptitle = it.selectFirst(".episode a span.name").text() | ||||
|             val secondtitle = it.selectFirst(".episode a span").text() | ||||
|                 .replace(Regex("(Episode (\\d+):|Episode (\\d+)-|Episode (\\d+))"),"") ?: "" | ||||
|             TvSeriesEpisode( | ||||
|             Episode( | ||||
|                 href, | ||||
|                 secondtitle+eptitle, | ||||
|                 season, | ||||
|                 episode, | ||||
|                 href, | ||||
|             ) | ||||
|         } | ||||
|         val tvType = if (url.contains("/movie/") && episodes.size == 1) TvType.Movie else TvType.TvSeries | ||||
|  |  | |||
|  | @ -104,11 +104,11 @@ class CinecalidadProvider:MainAPI() { | |||
|             val isValid = seasonid.size == 2 | ||||
|             val episode = if (isValid) seasonid.getOrNull(1) else null | ||||
|             val season = if (isValid) seasonid.getOrNull(0) else null | ||||
|             TvSeriesEpisode( | ||||
|             Episode( | ||||
|                 href, | ||||
|                 name, | ||||
|                 season, | ||||
|                 episode, | ||||
|                 href, | ||||
|                 if (epThumb.contains("svg")) null else epThumb | ||||
|             ) | ||||
|         } | ||||
|  |  | |||
|  | @ -1,14 +1,13 @@ | |||
| package com.lagradost.cloudstream3.movieproviders | ||||
| 
 | ||||
| import com.fasterxml.jackson.annotation.JsonProperty | ||||
| import com.fasterxml.jackson.module.kotlin.readValue | ||||
| import com.lagradost.cloudstream3.* | ||||
| import com.lagradost.cloudstream3.mvvm.logError | ||||
| import com.lagradost.cloudstream3.utils.* | ||||
| import com.lagradost.cloudstream3.utils.AppUtils.parseJson | ||||
| import java.util.* | ||||
| import com.lagradost.cloudstream3.utils.ExtractorLink | ||||
| import com.lagradost.cloudstream3.utils.loadExtractor | ||||
| 
 | ||||
| class CuevanaProvider:MainAPI() { | ||||
| class CuevanaProvider : MainAPI() { | ||||
|     override var mainUrl = "https://cuevana3.me" | ||||
|     override var name = "Cuevana" | ||||
|     override val lang = "es" | ||||
|  | @ -19,6 +18,7 @@ class CuevanaProvider:MainAPI() { | |||
|         TvType.Movie, | ||||
|         TvType.TvSeries, | ||||
|     ) | ||||
| 
 | ||||
|     override suspend fun getMainPage(): HomePageResponse { | ||||
|         val items = ArrayList<HomePageList>() | ||||
|         val urls = listOf( | ||||
|  | @ -28,20 +28,21 @@ class CuevanaProvider:MainAPI() { | |||
|         items.add( | ||||
|             HomePageList( | ||||
|                 "Series", | ||||
|                 app.get("$mainUrl/serie", timeout = 120).document.select("section.home-series li").map { | ||||
|                     val title = it.selectFirst("h2.Title").text() | ||||
|                     val poster = it.selectFirst("img.lazy").attr("data-src") | ||||
|                     val url = it.selectFirst("a").attr("href") | ||||
|                     TvSeriesSearchResponse( | ||||
|                         title, | ||||
|                         url, | ||||
|                         this.name, | ||||
|                         TvType.Anime, | ||||
|                         poster, | ||||
|                         null, | ||||
|                         null, | ||||
|                     ) | ||||
|                 }) | ||||
|                 app.get("$mainUrl/serie", timeout = 120).document.select("section.home-series li") | ||||
|                     .map { | ||||
|                         val title = it.selectFirst("h2.Title").text() | ||||
|                         val poster = it.selectFirst("img.lazy").attr("data-src") | ||||
|                         val url = it.selectFirst("a").attr("href") | ||||
|                         TvSeriesSearchResponse( | ||||
|                             title, | ||||
|                             url, | ||||
|                             this.name, | ||||
|                             TvType.Anime, | ||||
|                             poster, | ||||
|                             null, | ||||
|                             null, | ||||
|                         ) | ||||
|                     }) | ||||
|         ) | ||||
|         for ((url, name) in urls) { | ||||
|             try { | ||||
|  | @ -69,6 +70,7 @@ class CuevanaProvider:MainAPI() { | |||
|         if (items.size <= 0) throw ErrorLoadingException() | ||||
|         return HomePageResponse(items) | ||||
|     } | ||||
| 
 | ||||
|     override suspend fun search(query: String): List<SearchResponse> { | ||||
|         val url = "$mainUrl/?s=${query}" | ||||
|         val document = app.get(url).document | ||||
|  | @ -101,6 +103,7 @@ class CuevanaProvider:MainAPI() { | |||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     override suspend fun load(url: String): LoadResponse? { | ||||
|         val soup = app.get(url, timeout = 120).document | ||||
|         val title = soup.selectFirst("h1.Title").text() | ||||
|  | @ -108,30 +111,33 @@ class CuevanaProvider:MainAPI() { | |||
|         val poster: String? = soup.selectFirst(".movtv-info div.Image img").attr("data-src") | ||||
|         val year1 = soup.selectFirst("footer p.meta").toString() | ||||
|         val yearRegex = Regex("<span>(\\d+)</span>") | ||||
|         val yearf =  yearRegex.find(year1)?.destructured?.component1()?.replace(Regex("<span>|</span>"),"") | ||||
|         val yearf = | ||||
|             yearRegex.find(year1)?.destructured?.component1()?.replace(Regex("<span>|</span>"), "") | ||||
|         val year = if (yearf.isNullOrBlank()) null else yearf.toIntOrNull() | ||||
|         val episodes = soup.select(".all-episodes li.TPostMv article").map { li -> | ||||
|             val href = li.select("a").attr("href") | ||||
|             val epThumb = | ||||
|                 li.selectFirst("div.Image img").attr("data-src") ?: li.selectFirst("img.lazy").attr("data-srcc") | ||||
|                 li.selectFirst("div.Image img").attr("data-src") ?: li.selectFirst("img.lazy") | ||||
|                     .attr("data-srcc") | ||||
|             val seasonid = li.selectFirst("span.Year").text().let { str -> | ||||
|                 str.split("x").mapNotNull { subStr -> subStr.toIntOrNull() } | ||||
|             } | ||||
|             val isValid = seasonid.size == 2 | ||||
|             val episode = if (isValid) seasonid.getOrNull(1) else null | ||||
|             val season = if (isValid) seasonid.getOrNull(0) else null | ||||
|             TvSeriesEpisode( | ||||
|             Episode( | ||||
|                 href, | ||||
|                 null, | ||||
|                 season, | ||||
|                 episode, | ||||
|                 href, | ||||
|                 fixUrl(epThumb) | ||||
|             ) | ||||
|         } | ||||
|         val tags = soup.select("ul.InfoList li.AAIco-adjust:contains(Genero) a").map { it.text() } | ||||
|         val tvType = if (episodes.isEmpty()) TvType.Movie else TvType.TvSeries | ||||
|         val recelement = if (tvType == TvType.TvSeries) "main section div.series_listado.series div.xxx" | ||||
|         else "main section ul.MovieList li" | ||||
|         val recelement = | ||||
|             if (tvType == TvType.TvSeries) "main section div.series_listado.series div.xxx" | ||||
|             else "main section ul.MovieList li" | ||||
|         val recommendations = | ||||
|             soup.select(recelement).mapNotNull { element -> | ||||
|                 val recTitle = element.select("h2.Title").text() ?: return@mapNotNull null | ||||
|  | @ -183,6 +189,7 @@ class CuevanaProvider:MainAPI() { | |||
|     data class Femcuevana( | ||||
|         @JsonProperty("url") val url: String, | ||||
|     ) | ||||
| 
 | ||||
|     override suspend fun loadLinks( | ||||
|         data: String, | ||||
|         isCasting: Boolean, | ||||
|  | @ -192,24 +199,31 @@ class CuevanaProvider:MainAPI() { | |||
|         app.get(data).document.select("div.TPlayer.embed_div iframe").apmap { | ||||
|             val iframe = fixUrl(it.attr("data-src")) | ||||
|             if (iframe.contains("api.cuevana3.me/fembed/")) { | ||||
|                 val femregex = Regex("(https.\\/\\/api\\.cuevana3\\.me\\/fembed\\/\\?h=[a-zA-Z0-9]{0,8}[a-zA-Z0-9_-]+)") | ||||
|                 val femregex = | ||||
|                     Regex("(https.\\/\\/api\\.cuevana3\\.me\\/fembed\\/\\?h=[a-zA-Z0-9]{0,8}[a-zA-Z0-9_-]+)") | ||||
|                 femregex.findAll(iframe).map { femreg -> | ||||
|                     femreg.value | ||||
|                 }.toList().apmap { fem -> | ||||
|                     val key = fem.replace("https://api.cuevana3.me/fembed/?h=","") | ||||
|                     val url = app.post("https://api.cuevana3.me/fembed/api.php", allowRedirects = false, headers = mapOf("Host" to "api.cuevana3.me", | ||||
|                         "User-Agent" to USER_AGENT, | ||||
|                         "Accept" to "application/json, text/javascript, */*; q=0.01", | ||||
|                         "Accept-Language" to "en-US,en;q=0.5", | ||||
|                         "Content-Type" to "application/x-www-form-urlencoded; charset=UTF-8", | ||||
|                         "X-Requested-With" to "XMLHttpRequest", | ||||
|                         "Origin" to "https://api.cuevana3.me", | ||||
|                         "DNT" to "1", | ||||
|                         "Connection" to "keep-alive", | ||||
|                         "Sec-Fetch-Dest" to "empty", | ||||
|                         "Sec-Fetch-Mode" to "cors", | ||||
|                         "Sec-Fetch-Site" to "same-origin",), | ||||
|                         data = mapOf(Pair("h",key))).text | ||||
|                     val key = fem.replace("https://api.cuevana3.me/fembed/?h=", "") | ||||
|                     val url = app.post( | ||||
|                         "https://api.cuevana3.me/fembed/api.php", | ||||
|                         allowRedirects = false, | ||||
|                         headers = mapOf( | ||||
|                             "Host" to "api.cuevana3.me", | ||||
|                             "User-Agent" to USER_AGENT, | ||||
|                             "Accept" to "application/json, text/javascript, */*; q=0.01", | ||||
|                             "Accept-Language" to "en-US,en;q=0.5", | ||||
|                             "Content-Type" to "application/x-www-form-urlencoded; charset=UTF-8", | ||||
|                             "X-Requested-With" to "XMLHttpRequest", | ||||
|                             "Origin" to "https://api.cuevana3.me", | ||||
|                             "DNT" to "1", | ||||
|                             "Connection" to "keep-alive", | ||||
|                             "Sec-Fetch-Dest" to "empty", | ||||
|                             "Sec-Fetch-Mode" to "cors", | ||||
|                             "Sec-Fetch-Site" to "same-origin", | ||||
|                         ), | ||||
|                         data = mapOf(Pair("h", key)) | ||||
|                     ).text | ||||
|                     val json = parseJson<Femcuevana>(url) | ||||
|                     val link = json.url | ||||
|                     if (link.contains("fembed")) { | ||||
|  | @ -218,13 +232,16 @@ class CuevanaProvider:MainAPI() { | |||
|                 } | ||||
|             } | ||||
|             if (iframe.contains("tomatomatela")) { | ||||
|                 val tomatoRegex = Regex("(\\/\\/apialfa.tomatomatela.com\\/ir\\/player.php\\?h=[a-zA-Z0-9]{0,8}[a-zA-Z0-9_-]+)") | ||||
|                 val tomatoRegex = | ||||
|                     Regex("(\\/\\/apialfa.tomatomatela.com\\/ir\\/player.php\\?h=[a-zA-Z0-9]{0,8}[a-zA-Z0-9_-]+)") | ||||
|                 tomatoRegex.findAll(iframe).map { tomreg -> | ||||
|                     tomreg.value | ||||
|                 }.toList().apmap { tom -> | ||||
|                     val tomkey = tom.replace("//apialfa.tomatomatela.com/ir/player.php?h=","") | ||||
|                     app.post("https://apialfa.tomatomatela.com/ir/rd.php", allowRedirects = false, | ||||
|                         headers = mapOf("Host" to "apialfa.tomatomatela.com", | ||||
|                     val tomkey = tom.replace("//apialfa.tomatomatela.com/ir/player.php?h=", "") | ||||
|                     app.post( | ||||
|                         "https://apialfa.tomatomatela.com/ir/rd.php", allowRedirects = false, | ||||
|                         headers = mapOf( | ||||
|                             "Host" to "apialfa.tomatomatela.com", | ||||
|                             "User-Agent" to USER_AGENT, | ||||
|                             "Accept" to "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8", | ||||
|                             "Accept-Language" to "en-US,en;q=0.5", | ||||
|  | @ -235,16 +252,21 @@ class CuevanaProvider:MainAPI() { | |||
|                             "Upgrade-Insecure-Requests" to "1", | ||||
|                             "Sec-Fetch-Dest" to "iframe", | ||||
|                             "Sec-Fetch-Mode" to "navigate", | ||||
|                             "Sec-Fetch-Site" to "same-origin",), | ||||
|                         data = mapOf(Pair("url",tomkey)) | ||||
|                             "Sec-Fetch-Site" to "same-origin", | ||||
|                         ), | ||||
|                         data = mapOf(Pair("url", tomkey)) | ||||
|                     ).response.headers.values("location").apmap { loc -> | ||||
|                         if (loc.contains("goto_ddh.php")) { | ||||
|                             val gotoregex = Regex("(\\/\\/api.cuevana3.me\\/ir\\/goto_ddh.php\\?h=[a-zA-Z0-9]{0,8}[a-zA-Z0-9_-]+)") | ||||
|                             val gotoregex = | ||||
|                                 Regex("(\\/\\/api.cuevana3.me\\/ir\\/goto_ddh.php\\?h=[a-zA-Z0-9]{0,8}[a-zA-Z0-9_-]+)") | ||||
|                             gotoregex.findAll(loc).map { goreg -> | ||||
|                                 goreg.value.replace("//api.cuevana3.me/ir/goto_ddh.php?h=","") | ||||
|                                 goreg.value.replace("//api.cuevana3.me/ir/goto_ddh.php?h=", "") | ||||
|                             }.toList().apmap { gotolink -> | ||||
|                                 app.post("https://api.cuevana3.me/ir/redirect_ddh.php", allowRedirects = false, | ||||
|                                     headers = mapOf("Host" to "api.cuevana3.me", | ||||
|                                 app.post( | ||||
|                                     "https://api.cuevana3.me/ir/redirect_ddh.php", | ||||
|                                     allowRedirects = false, | ||||
|                                     headers = mapOf( | ||||
|                                         "Host" to "api.cuevana3.me", | ||||
|                                         "User-Agent" to USER_AGENT, | ||||
|                                         "Accept" to "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8", | ||||
|                                         "Accept-Language" to "en-US,en;q=0.5", | ||||
|  | @ -255,20 +277,24 @@ class CuevanaProvider:MainAPI() { | |||
|                                         "Upgrade-Insecure-Requests" to "1", | ||||
|                                         "Sec-Fetch-Dest" to "iframe", | ||||
|                                         "Sec-Fetch-Mode" to "navigate", | ||||
|                                         "Sec-Fetch-Site" to "same-origin",), | ||||
|                                     data = mapOf(Pair("url",gotolink)) | ||||
|                                         "Sec-Fetch-Site" to "same-origin", | ||||
|                                     ), | ||||
|                                     data = mapOf(Pair("url", gotolink)) | ||||
|                                 ).response.headers.values("location").apmap { golink -> | ||||
|                                     loadExtractor(golink, data, callback) | ||||
|                                 } | ||||
|                             } | ||||
|                         } | ||||
|                         if (loc.contains("index.php?h=")) { | ||||
|                             val indexRegex = Regex("(\\/\\/api.cuevana3.me\\/sc\\/index.php\\?h=[a-zA-Z0-9]{0,8}[a-zA-Z0-9_-]+)") | ||||
|                             val indexRegex = | ||||
|                                 Regex("(\\/\\/api.cuevana3.me\\/sc\\/index.php\\?h=[a-zA-Z0-9]{0,8}[a-zA-Z0-9_-]+)") | ||||
|                             indexRegex.findAll(loc).map { indreg -> | ||||
|                                 indreg.value.replace("//api.cuevana3.me/sc/index.php?h=","") | ||||
|                                 indreg.value.replace("//api.cuevana3.me/sc/index.php?h=", "") | ||||
|                             }.toList().apmap { inlink -> | ||||
|                                 app.post("https://api.cuevana3.me/sc/r.php", allowRedirects = false, | ||||
|                                     headers = mapOf("Host" to "api.cuevana3.me", | ||||
|                                 app.post( | ||||
|                                     "https://api.cuevana3.me/sc/r.php", allowRedirects = false, | ||||
|                                     headers = mapOf( | ||||
|                                         "Host" to "api.cuevana3.me", | ||||
|                                         "User-Agent" to USER_AGENT, | ||||
|                                         "Accept" to "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8", | ||||
|                                         "Accept-Language" to "en-US,en;q=0.5", | ||||
|  | @ -281,8 +307,9 @@ class CuevanaProvider:MainAPI() { | |||
|                                         "Sec-Fetch-Dest" to "iframe", | ||||
|                                         "Sec-Fetch-Mode" to "navigate", | ||||
|                                         "Sec-Fetch-Site" to "same-origin", | ||||
|                                         "Sec-Fetch-User" to "?1",), | ||||
|                                     data = mapOf(Pair("h",inlink)) | ||||
|                                         "Sec-Fetch-User" to "?1", | ||||
|                                     ), | ||||
|                                     data = mapOf(Pair("h", inlink)) | ||||
|                                 ).response.headers.values("location").apmap { link -> | ||||
|                                     loadExtractor(link, data, callback) | ||||
|                                 } | ||||
|  |  | |||
|  | @ -133,7 +133,7 @@ class DoramasYTProvider : MainAPI() { | |||
|             val name = it.selectFirst(".dtlsflim p").text() | ||||
|             val link = it.selectFirst("a").attr("href") | ||||
|             val epThumb = it.selectFirst(".flimimg img.img1").attr("src") | ||||
|             AnimeEpisode(link, name, posterUrl = epThumb) | ||||
|             Episode(link, name, posterUrl = epThumb) | ||||
|         } | ||||
|         return newAnimeLoadResponse(title, url, getType(type)) { | ||||
|             posterUrl = poster | ||||
|  |  | |||
|  | @ -118,7 +118,7 @@ class DramaSeeProvider : MainAPI() { | |||
|         } | ||||
| 
 | ||||
|         // Episodes Links | ||||
|         val episodeList = ArrayList<TvSeriesEpisode>() | ||||
|         val episodeList = ArrayList<Episode>() | ||||
|         body?.select("ul.episodes > li")?.forEach { ep -> | ||||
|             val innerA = ep.select("a") ?: return@forEach | ||||
|             val count = innerA.select("span.episode")?.text()?.toIntOrNull() ?: 0 | ||||
|  | @ -141,7 +141,7 @@ class DramaSeeProvider : MainAPI() { | |||
| 
 | ||||
|                     //Log.i(this.name, "Result => (listOfLinks) ${listOfLinks.toJson()}") | ||||
|                     episodeList.add( | ||||
|                         TvSeriesEpisode( | ||||
|                         Episode( | ||||
|                             name = null, | ||||
|                             season = null, | ||||
|                             episode = count, | ||||
|  |  | |||
|  | @ -1,9 +1,9 @@ | |||
| package com.lagradost.cloudstream3.movieproviders | ||||
| 
 | ||||
| import com.lagradost.cloudstream3.* | ||||
| import com.lagradost.cloudstream3.utils.ExtractorLink | ||||
| import com.lagradost.cloudstream3.utils.AppUtils.parseJson | ||||
| import com.fasterxml.jackson.annotation.JsonProperty | ||||
| import com.lagradost.cloudstream3.* | ||||
| import com.lagradost.cloudstream3.utils.AppUtils.parseJson | ||||
| import com.lagradost.cloudstream3.utils.ExtractorLink | ||||
| import org.jsoup.nodes.Element | ||||
| 
 | ||||
| class EgyBestProvider : MainAPI() { | ||||
|  | @ -109,7 +109,7 @@ class EgyBestProvider : MainAPI() { | |||
|                 this.actors = actors | ||||
|             } | ||||
|         } else { | ||||
|             val episodes = ArrayList<TvSeriesEpisode>() | ||||
|             val episodes = ArrayList<Episode>() | ||||
|             doc.select("#mainLoad > div:nth-child(2) > div.h_scroll > div a").map { | ||||
|                 it.attr("href") | ||||
|             }.apmap { | ||||
|  | @ -118,13 +118,11 @@ class EgyBestProvider : MainAPI() { | |||
|                 d.select("#mainLoad > div:nth-child(3) > div.movies_small a").map { eit -> | ||||
|                     val ep = Regex("ep-(.....)").find(eit.attr("href"))?.groupValues?.getOrNull(1)?.getIntFromText() | ||||
|                     episodes.add( | ||||
|                         TvSeriesEpisode( | ||||
|                         Episode( | ||||
|                             eit.attr("href"), | ||||
|                             eit.select("span.title").text(), | ||||
|                             season, | ||||
|                             ep, | ||||
|                             eit.attr("href"), | ||||
|                             null, | ||||
|                             null | ||||
|                         ) | ||||
|                     ) | ||||
|                 } | ||||
|  |  | |||
|  | @ -2,8 +2,8 @@ package com.lagradost.cloudstream3.movieproviders | |||
| 
 | ||||
| import com.lagradost.cloudstream3.* | ||||
| import com.lagradost.cloudstream3.mvvm.logError | ||||
| import com.lagradost.cloudstream3.utils.* | ||||
| import java.util.* | ||||
| import com.lagradost.cloudstream3.utils.ExtractorLink | ||||
| import com.lagradost.cloudstream3.utils.loadExtractor | ||||
| 
 | ||||
| class EntrepeliculasyseriesProvider:MainAPI() { | ||||
|     override var mainUrl = "https://entrepeliculasyseries.nu" | ||||
|  | @ -102,11 +102,11 @@ class EntrepeliculasyseriesProvider:MainAPI() { | |||
|             val isValid = seasonid.size == 2 | ||||
|             val episode = if (isValid) seasonid.getOrNull(1) else null | ||||
|             val season = if (isValid) seasonid.getOrNull(0) else null | ||||
|             TvSeriesEpisode( | ||||
|             Episode( | ||||
|                 href, | ||||
|                 null, | ||||
|                 season, | ||||
|                 episode, | ||||
|                 href, | ||||
|                 fixUrl(epThumb) | ||||
|             ) | ||||
|         } | ||||
|  |  | |||
|  | @ -100,11 +100,11 @@ class FilmanProvider : MainAPI() { | |||
|             val e = episode.text() | ||||
|             val regex = Regex("""\[s(\d{1,3})e(\d{1,3})]""").find(e) ?: return@mapNotNull null | ||||
|             val eid = regex.groups | ||||
|             TvSeriesEpisode( | ||||
|             Episode( | ||||
|                 episode.attr("href"), | ||||
|                 e.split("]")[1].trim(), | ||||
|                 eid[1]?.value?.toInt(), | ||||
|                 eid[2]?.value?.toInt(), | ||||
|                 episode.attr("href"), | ||||
|             ) | ||||
|         }.toMutableList() | ||||
| 
 | ||||
|  |  | |||
|  | @ -139,7 +139,7 @@ class IHaveNoTvProvider : MainAPI() { | |||
|         val episodes = if (isSeries) { | ||||
|             container?.select(".episode")?.map { ep -> | ||||
|                 val thumb = ep.selectFirst("img").attr("src") | ||||
|                 val epTitle = ep.selectFirst("a[title]").attr("title") | ||||
| 
 | ||||
|                 val epLink = fixUrl(ep.selectFirst("a[title]").attr("href")) | ||||
|                 val (season, epNum) = if (ep.selectFirst(".episodeMeta > strong") != null && | ||||
|                     ep.selectFirst(".episodeMeta > strong").html().contains("S") | ||||
|  | @ -150,7 +150,7 @@ class IHaveNoTvProvider : MainAPI() { | |||
|                         split?.get(1)?.toIntOrNull() | ||||
|                     ) | ||||
|                 } else Pair<Int?, Int?>(null, null) | ||||
|                 val epDescription = ep.selectFirst(".episodeSynopsis")?.text() | ||||
| 
 | ||||
|                 year = Regex("""•?\s+(\d{4})\s+•""").find( | ||||
|                     ep.selectFirst(".episodeMeta").text() | ||||
|                 )?.destructured?.component1()?.toIntOrNull() | ||||
|  | @ -158,16 +158,13 @@ class IHaveNoTvProvider : MainAPI() { | |||
|                 categories.addAll( | ||||
|                     ep.select(".episodeMeta > a[href*=\"/category/\"]").map { it.text().trim() }) | ||||
| 
 | ||||
|                 TvSeriesEpisode( | ||||
|                     epTitle, | ||||
|                     season, | ||||
|                     epNum, | ||||
|                     epLink, | ||||
|                     thumb, | ||||
|                     null, | ||||
|                     null, | ||||
|                     epDescription | ||||
|                 ) | ||||
|                 newEpisode(epLink) { | ||||
|                     this.name = ep.selectFirst("a[title]").attr("title") | ||||
|                     this.season = season | ||||
|                     this.episode = epNum | ||||
|                     this.posterUrl = thumb | ||||
|                     this.description = ep.selectFirst(".episodeSynopsis")?.text() | ||||
|                 } | ||||
|             } | ||||
|         } else { | ||||
|             listOf(MovieLoadResponse( | ||||
|  | @ -188,7 +185,7 @@ class IHaveNoTvProvider : MainAPI() { | |||
|         } | ||||
| 
 | ||||
|         val poster = episodes?.firstOrNull().let { | ||||
|             if (isSeries && it != null) (it as TvSeriesEpisode).posterUrl | ||||
|             if (isSeries && it != null) (it as Episode).posterUrl | ||||
|             else null | ||||
|         } | ||||
| 
 | ||||
|  | @ -197,7 +194,7 @@ class IHaveNoTvProvider : MainAPI() { | |||
|             url, | ||||
|             this.name, | ||||
|             TvType.TvSeries, | ||||
|             episodes!!.map { it as TvSeriesEpisode }, | ||||
|             episodes!!.map { it as Episode }, | ||||
|             poster, | ||||
|             year, | ||||
|             description, | ||||
|  |  | |||
|  | @ -173,7 +173,7 @@ class KdramaHoodProvider : MainAPI() { | |||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             TvSeriesEpisode( | ||||
|             Episode( | ||||
|                 name = null, | ||||
|                 season = null, | ||||
|                 episode = count, | ||||
|  |  | |||
|  | @ -273,11 +273,11 @@ class LookMovieProvider : MainAPI() { | |||
|                     ).toJson() | ||||
| 
 | ||||
| 
 | ||||
|                 TvSeriesEpisode( | ||||
|                 Episode( | ||||
|                     localData, | ||||
|                     it.title, | ||||
|                     it.season.toIntOrNull(), | ||||
|                     it.episode.toIntOrNull(), | ||||
|                     localData | ||||
|                 ) | ||||
|             }.toList() | ||||
| 
 | ||||
|  |  | |||
|  | @ -5,7 +5,6 @@ import com.fasterxml.jackson.module.kotlin.readValue | |||
| import com.lagradost.cloudstream3.* | ||||
| import com.lagradost.cloudstream3.LoadResponse.Companion.addImdbUrl | ||||
| import com.lagradost.cloudstream3.utils.AppUtils.parseJson | ||||
| import com.lagradost.cloudstream3.utils.AppUtils.toJson | ||||
| import com.lagradost.cloudstream3.utils.ExtractorLink | ||||
| import com.lagradost.cloudstream3.utils.getQualityFromName | ||||
| import org.jsoup.Jsoup | ||||
|  | @ -87,7 +86,7 @@ class MeloMovieProvider : MainAPI() { | |||
|         return url | ||||
|     } | ||||
| 
 | ||||
|     private fun serializeData(element: Element): String { | ||||
|     private fun serializeData(element: Element): List<MeloMovieProvider.MeloMovieLink> { | ||||
|         val eps = element.select("> tbody > tr") | ||||
|         val parsed = eps.map { | ||||
|             try { | ||||
|  | @ -99,7 +98,7 @@ class MeloMovieProvider : MainAPI() { | |||
|                 MeloMovieLink("", "") | ||||
|             } | ||||
|         }.filter { it.link != "" && it.name != "" } | ||||
|         return parsed.toJson() | ||||
|         return parsed | ||||
|     } | ||||
| 
 | ||||
|     override suspend fun loadLinks( | ||||
|  | @ -157,7 +156,7 @@ class MeloMovieProvider : MainAPI() { | |||
|                 addImdbUrl(imdbUrl) | ||||
|             } | ||||
|         } else if (type == 2) { | ||||
|             val episodes = ArrayList<TvSeriesEpisode>() | ||||
|             val episodes = ArrayList<Episode>() | ||||
|             val seasons = document.select("div.accordion__card") | ||||
|                 ?: throw ErrorLoadingException("No episodes found") | ||||
|             for (s in seasons) { | ||||
|  | @ -172,7 +171,10 @@ class MeloMovieProvider : MainAPI() { | |||
|                     val links = | ||||
|                         e.selectFirst("> div.collapse > div > table.accordion__list") ?: continue | ||||
|                     val data = serializeData(links) | ||||
|                     episodes.add(TvSeriesEpisode(null, season, episode, data)) | ||||
|                     episodes.add(newEpisode(data) { | ||||
|                         this.season = season | ||||
|                         this.episode = episode | ||||
|                     }) | ||||
|                 } | ||||
|             } | ||||
|             episodes.reverse() | ||||
|  |  | |||
|  | @ -31,7 +31,7 @@ class MyCimaProvider : MainAPI() { | |||
|         val title = select("div.Thumb--GridItem strong").text() | ||||
|             .replace("$year", "") | ||||
|             .replace("مشاهدة|فيلم|مسلسل|مترجم".toRegex(), "") | ||||
|             .replace("( نسخة مدبلجة )"," ( نسخة مدبلجة ) ") | ||||
|             .replace("( نسخة مدبلجة )", " ( نسخة مدبلجة ) ") | ||||
|         // If you need to differentiate use the url. | ||||
|         return MovieSearchResponse( | ||||
|             title, | ||||
|  | @ -47,8 +47,8 @@ class MyCimaProvider : MainAPI() { | |||
|     override suspend fun getMainPage(): HomePageResponse { | ||||
|         // Title, Url | ||||
|         val moviesUrl = listOf( | ||||
|             "Movies" to "$mainUrl/movies/page/"+(0..25).random(), | ||||
|             "Series" to "$mainUrl/seriestv/new/page/"+(0..25).random() | ||||
|             "Movies" to "$mainUrl/movies/page/" + (0..25).random(), | ||||
|             "Series" to "$mainUrl/seriestv/new/page/" + (0..25).random() | ||||
|         ) | ||||
|         val pages = moviesUrl.apmap { | ||||
|             val doc = app.get(it.second).document | ||||
|  | @ -61,21 +61,23 @@ class MyCimaProvider : MainAPI() { | |||
|     } | ||||
| 
 | ||||
|     override suspend fun search(query: String): List<SearchResponse> { | ||||
|         val q = query.replace(" ","%20") | ||||
|         val q = query.replace(" ", "%20") | ||||
|         val result = arrayListOf<SearchResponse>() | ||||
|         listOf("$mainUrl/search/$q", | ||||
|         listOf( | ||||
|             "$mainUrl/search/$q", | ||||
|             "$mainUrl/search/$q/list/series/", | ||||
|             "$mainUrl/search/$q/list/anime/").apmap { url -> | ||||
|             "$mainUrl/search/$q/list/anime/" | ||||
|         ).apmap { url -> | ||||
|             val d = app.get(url).document | ||||
|             d.select("div.Grid--MycimaPosts div.GridItem").mapNotNull { | ||||
|                 if(it.text().contains("اعلان")) return@mapNotNull null | ||||
|                     it.toSearchResponse()?.let { it1 -> result.add(it1) } | ||||
|                 } | ||||
|                 if (it.text().contains("اعلان")) return@mapNotNull null | ||||
|                 it.toSearchResponse()?.let { it1 -> result.add(it1) } | ||||
|             } | ||||
|         } | ||||
|         return result.distinct().sortedBy { it.name } | ||||
|     } | ||||
| 
 | ||||
|     data class MoreEPS ( | ||||
|     data class MoreEPS( | ||||
|         val output: String | ||||
|     ) | ||||
| 
 | ||||
|  | @ -86,7 +88,8 @@ class MyCimaProvider : MainAPI() { | |||
|             doc.select("mycima.separated--top")?.attr("data-lazy-style")?.getImageURL() | ||||
|                 ?.ifEmpty { doc.select("meta[itemprop=\"thumbnailUrl\"]")?.attr("content") } | ||||
|                 ?.ifEmpty { doc.select("mycima.separated--top")?.attr("style")?.getImageURL() } | ||||
|         val year = doc.select("div.Title--Content--Single-begin h1 a.unline")?.text()?.getIntFromText() | ||||
|         val year = | ||||
|             doc.select("div.Title--Content--Single-begin h1 a.unline")?.text()?.getIntFromText() | ||||
|         val title = doc.select("div.Title--Content--Single-begin h1").text() | ||||
|             .replace("($year)", "") | ||||
|             .replace("مشاهدة|فيلم|مسلسل|مترجم|انمي".toRegex(), "") | ||||
|  | @ -96,7 +99,8 @@ class MyCimaProvider : MainAPI() { | |||
|                 it.text().contains("المدة") | ||||
|             }?.text()?.getIntFromText() | ||||
| 
 | ||||
|         val synopsis = doc.select("div.StoryMovieContent").text().ifEmpty { doc.select("div.PostItemContent").text() } | ||||
|         val synopsis = doc.select("div.StoryMovieContent").text() | ||||
|             .ifEmpty { doc.select("div.PostItemContent").text() } | ||||
| 
 | ||||
|         val tags = doc.select("li:nth-child(3) > p > a").map { it.text() } | ||||
| 
 | ||||
|  | @ -107,9 +111,10 @@ class MyCimaProvider : MainAPI() { | |||
|                 ?: return@mapNotNull null | ||||
|             Actor(name, image) | ||||
|         } | ||||
|         val recommendations = doc.select("div.Grid--MycimaPosts div.GridItem")?.mapNotNull { element -> | ||||
|             element.toSearchResponse() | ||||
|         } | ||||
|         val recommendations = | ||||
|             doc.select("div.Grid--MycimaPosts div.GridItem")?.mapNotNull { element -> | ||||
|                 element.toSearchResponse() | ||||
|             } | ||||
| 
 | ||||
|         return if (isMovie) { | ||||
|             newMovieLoadResponse( | ||||
|  | @ -127,55 +132,165 @@ class MyCimaProvider : MainAPI() { | |||
|                 addActors(actors) | ||||
|             } | ||||
|         } else { | ||||
|             val episodes = ArrayList<TvSeriesEpisode>() | ||||
|             val episodes = ArrayList<Episode>() | ||||
|             val seasons = doc.select("div.List--Seasons--Episodes a").not(".selected").map { | ||||
|                 it.attr("href") | ||||
|             } | ||||
|             val moreButton = doc.select("div.MoreEpisodes--Button") | ||||
|             val season = doc.select("div.List--Seasons--Episodes a.selected").text().getIntFromText() | ||||
|             val season = | ||||
|                 doc.select("div.List--Seasons--Episodes a.selected").text().getIntFromText() | ||||
|             doc.select("div.Seasons--Episodes div.Episodes--Seasons--Episodes a") | ||||
|                 .apmap { episodes.add(TvSeriesEpisode(it.text(), season, it.text().getIntFromText(), it.attr("href"), null, null))} | ||||
|             if(moreButton.isNotEmpty()) { | ||||
|                 .apmap { | ||||
|                     episodes.add( | ||||
|                         Episode( | ||||
|                             it.attr("href"), | ||||
|                             it.text(), | ||||
|                             season, | ||||
|                             it.text().getIntFromText(), | ||||
|                         ) | ||||
|                     ) | ||||
|                 } | ||||
|             if (moreButton.isNotEmpty()) { | ||||
|                 val n = doc.select("div.Seasons--Episodes div.Episodes--Seasons--Episodes a").size | ||||
|                 val totals = doc.select("div.Episodes--Seasons--Episodes a").first().text().getIntFromText() | ||||
|                 val mEPS = arrayListOf(n, n+40, n+80, n+120, n+160, n+200, n+240, n+280, n+320, n+360, n+400, n+440, n+480, n+520, n+660, n+700, n+740, n+780, n+820, n+860, n+900, n+940, n+980, n+1020, n+1060, n+1100, n+1140, n+1180, n+1220, totals) | ||||
|                 val totals = | ||||
|                     doc.select("div.Episodes--Seasons--Episodes a").first().text().getIntFromText() | ||||
|                 val mEPS = arrayListOf( | ||||
|                     n, | ||||
|                     n + 40, | ||||
|                     n + 80, | ||||
|                     n + 120, | ||||
|                     n + 160, | ||||
|                     n + 200, | ||||
|                     n + 240, | ||||
|                     n + 280, | ||||
|                     n + 320, | ||||
|                     n + 360, | ||||
|                     n + 400, | ||||
|                     n + 440, | ||||
|                     n + 480, | ||||
|                     n + 520, | ||||
|                     n + 660, | ||||
|                     n + 700, | ||||
|                     n + 740, | ||||
|                     n + 780, | ||||
|                     n + 820, | ||||
|                     n + 860, | ||||
|                     n + 900, | ||||
|                     n + 940, | ||||
|                     n + 980, | ||||
|                     n + 1020, | ||||
|                     n + 1060, | ||||
|                     n + 1100, | ||||
|                     n + 1140, | ||||
|                     n + 1180, | ||||
|                     n + 1220, | ||||
|                     totals | ||||
|                 ) | ||||
|                 mEPS.apmap { it -> | ||||
|                         if (it != null) { | ||||
|                             if(it > totals!!) return@apmap | ||||
|                             val ajaxURL = "$mainUrl/AjaxCenter/MoreEpisodes/${moreButton.attr("data-term")}/$it" | ||||
|                             val jsonResponse = app.get(ajaxURL) | ||||
|                             val json = parseJson<MoreEPS>(jsonResponse.text) | ||||
|                             val document = Jsoup.parse(json.output?.replace("""\""", "")) | ||||
|                             document.select("a").map { episodes.add(TvSeriesEpisode(it.text(), season, it.text().getIntFromText(), it.attr("href"), null, null)) } | ||||
|                     if (it != null) { | ||||
|                         if (it > totals!!) return@apmap | ||||
|                         val ajaxURL = | ||||
|                             "$mainUrl/AjaxCenter/MoreEpisodes/${moreButton.attr("data-term")}/$it" | ||||
|                         val jsonResponse = app.get(ajaxURL) | ||||
|                         val json = parseJson<MoreEPS>(jsonResponse.text) | ||||
|                         val document = Jsoup.parse(json.output?.replace("""\""", "")) | ||||
|                         document.select("a").map { | ||||
|                             episodes.add( | ||||
|                                 Episode( | ||||
|                                     it.attr("href"), | ||||
|                                     it.text(), | ||||
|                                     season, | ||||
|                                     it.text().getIntFromText(), | ||||
|                                 ) | ||||
|                             ) | ||||
|                         } | ||||
|                     } | ||||
|             } | ||||
|             if(seasons.isNotEmpty()) { | ||||
|                     seasons.apmap { surl -> | ||||
|                         if(surl.contains("%d9%85%d8%af%d8%a8%d9%84%d8%ac")) return@apmap | ||||
|                             val seasonsite = app.get(surl).document | ||||
|                             val fmoreButton = seasonsite.select("div.MoreEpisodes--Button") | ||||
|                             val fseason = seasonsite.select("div.List--Seasons--Episodes a.selected").text().getIntFromText() ?: 1 | ||||
|                             seasonsite.select("div.Seasons--Episodes div.Episodes--Seasons--Episodes a") | ||||
|                                         .apmap { episodes.add(TvSeriesEpisode(it.text(), fseason, it.text().getIntFromText(), it.attr("href"), null, null))} | ||||
|                             if(fmoreButton.isNotEmpty()) { | ||||
|                                 val n = seasonsite.select("div.Seasons--Episodes div.Episodes--Seasons--Episodes a").size | ||||
|                                 val totals = seasonsite.select("div.Episodes--Seasons--Episodes a").first().text().getIntFromText() | ||||
|                                 val mEPS = arrayListOf(n, n+40, n+80, n+120, n+160, n+200, n+240, n+280, n+320, n+360, n+400, n+440, n+480, n+520, n+660, n+700, n+740, n+780, n+820, n+860, n+900, n+940, n+980, n+1020, n+1060, n+1100, n+1140, n+1180, n+1220, totals) | ||||
|                                 mEPS.apmap { it -> | ||||
|                                         if (it != null) { | ||||
|                                             if(it > totals!!) return@apmap | ||||
|                                             val ajaxURL = "$mainUrl/AjaxCenter/MoreEpisodes/${fmoreButton.attr("data-term")}/$it" | ||||
|                                             val jsonResponse = app.get(ajaxURL) | ||||
|                                             val json = parseJson<MoreEPS>(jsonResponse.text) | ||||
|                                             val document = Jsoup.parse(json.output?.replace("""\""", "")) | ||||
|                                             document.select("a").map { episodes.add(TvSeriesEpisode(it.text(), fseason, it.text().getIntFromText(), it.attr("href"), null, null)) } | ||||
|                                         } | ||||
|                                 } | ||||
|                             } else return@apmap | ||||
|                     } | ||||
|                 } | ||||
|             newTvSeriesLoadResponse(title, url, TvType.TvSeries, episodes.distinct().sortedBy { it.episode }) { | ||||
|             } | ||||
|             if (seasons.isNotEmpty()) { | ||||
|                 seasons.apmap { surl -> | ||||
|                     if (surl.contains("%d9%85%d8%af%d8%a8%d9%84%d8%ac")) return@apmap | ||||
|                     val seasonsite = app.get(surl).document | ||||
|                     val fmoreButton = seasonsite.select("div.MoreEpisodes--Button") | ||||
|                     val fseason = seasonsite.select("div.List--Seasons--Episodes a.selected").text() | ||||
|                         .getIntFromText() ?: 1 | ||||
|                     seasonsite.select("div.Seasons--Episodes div.Episodes--Seasons--Episodes a") | ||||
|                         .map { | ||||
|                             episodes.add( | ||||
|                                 Episode( | ||||
|                                     it.attr("href"), | ||||
|                                     it.text(), | ||||
|                                     fseason, | ||||
|                                     it.text().getIntFromText(), | ||||
|                                 ) | ||||
|                             ) | ||||
|                         } | ||||
|                     if (fmoreButton.isNotEmpty()) { | ||||
|                         val n = | ||||
|                             seasonsite.select("div.Seasons--Episodes div.Episodes--Seasons--Episodes a").size | ||||
|                         val totals = | ||||
|                             seasonsite.select("div.Episodes--Seasons--Episodes a").first().text() | ||||
|                                 .getIntFromText() | ||||
|                         val mEPS = arrayListOf( | ||||
|                             n, | ||||
|                             n + 40, | ||||
|                             n + 80, | ||||
|                             n + 120, | ||||
|                             n + 160, | ||||
|                             n + 200, | ||||
|                             n + 240, | ||||
|                             n + 280, | ||||
|                             n + 320, | ||||
|                             n + 360, | ||||
|                             n + 400, | ||||
|                             n + 440, | ||||
|                             n + 480, | ||||
|                             n + 520, | ||||
|                             n + 660, | ||||
|                             n + 700, | ||||
|                             n + 740, | ||||
|                             n + 780, | ||||
|                             n + 820, | ||||
|                             n + 860, | ||||
|                             n + 900, | ||||
|                             n + 940, | ||||
|                             n + 980, | ||||
|                             n + 1020, | ||||
|                             n + 1060, | ||||
|                             n + 1100, | ||||
|                             n + 1140, | ||||
|                             n + 1180, | ||||
|                             n + 1220, | ||||
|                             totals | ||||
|                         ) | ||||
|                         mEPS.apmap { it -> | ||||
|                             if (it != null) { | ||||
|                                 if (it > totals!!) return@apmap | ||||
|                                 val ajaxURL = | ||||
|                                     "$mainUrl/AjaxCenter/MoreEpisodes/${fmoreButton.attr("data-term")}/$it" | ||||
|                                 val jsonResponse = app.get(ajaxURL) | ||||
|                                 val json = parseJson<MoreEPS>(jsonResponse.text) | ||||
|                                 val document = Jsoup.parse(json.output?.replace("""\""", "")) | ||||
|                                 document.select("a").map { | ||||
|                                     episodes.add( | ||||
|                                         Episode( | ||||
|                                             it.attr("href"), | ||||
|                                             it.text(), | ||||
|                                             fseason, | ||||
|                                             it.text().getIntFromText(), | ||||
|                                         ) | ||||
|                                     ) | ||||
|                                 } | ||||
|                             } | ||||
|                         } | ||||
|                     } else return@apmap | ||||
|                 } | ||||
|             } | ||||
|             newTvSeriesLoadResponse( | ||||
|                 title, | ||||
|                 url, | ||||
|                 TvType.TvSeries, | ||||
|                 episodes.distinct().sortedBy { it.episode }) { | ||||
|                 this.duration = duration | ||||
|                 this.posterUrl = posterUrl | ||||
|                 this.tags = tags | ||||
|  | @ -186,6 +301,7 @@ class MyCimaProvider : MainAPI() { | |||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     override suspend fun loadLinks( | ||||
|         data: String, | ||||
|         isCasting: Boolean, | ||||
|  | @ -194,18 +310,20 @@ class MyCimaProvider : MainAPI() { | |||
|     ): Boolean { | ||||
|         app.get(data).document | ||||
|             .select("ul.List--Download--Mycima--Single:nth-child(2) li").map { | ||||
|             it.select("a").map { linkElement -> | ||||
|                 callback.invoke( | ||||
|                     ExtractorLink( | ||||
|                         this.name, | ||||
|                         this.name + " - ${linkElement.select("resolution").text().getIntFromText()}p", | ||||
|                         linkElement.attr("href"), | ||||
|                         this.mainUrl, | ||||
|                         2 | ||||
|                 it.select("a").map { linkElement -> | ||||
|                     callback.invoke( | ||||
|                         ExtractorLink( | ||||
|                             this.name, | ||||
|                             this.name + " - ${ | ||||
|                                 linkElement.select("resolution").text().getIntFromText() | ||||
|                             }p", | ||||
|                             linkElement.attr("href"), | ||||
|                             this.mainUrl, | ||||
|                             2 | ||||
|                         ) | ||||
|                     ) | ||||
|                 ) | ||||
|             } | ||||
|         }.flatten() | ||||
|                 } | ||||
|             }.flatten() | ||||
|         return true | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -2,9 +2,7 @@ package com.lagradost.cloudstream3.movieproviders | |||
| 
 | ||||
| import com.lagradost.cloudstream3.* | ||||
| import com.lagradost.cloudstream3.utils.ExtractorLink | ||||
| import com.lagradost.cloudstream3.utils.extractorApis | ||||
| import com.lagradost.cloudstream3.utils.loadExtractor | ||||
| import kotlin.collections.ArrayList | ||||
| 
 | ||||
| class PeliSmartProvider: MainAPI() { | ||||
|     override var mainUrl = "https://pelismart.com" | ||||
|  | @ -105,11 +103,11 @@ class PeliSmartProvider: MainAPI() { | |||
|             val isValid = seasonid?.size == 2 | ||||
|             val episode = if (isValid) seasonid?.getOrNull(1) else null | ||||
|             val season = if (isValid) seasonid?.getOrNull(0) else null | ||||
|                 TvSeriesEpisode( | ||||
|                 Episode( | ||||
|                     href, | ||||
|                     name, | ||||
|                     season, | ||||
|                     episode, | ||||
|                     href, | ||||
|                 ) | ||||
|         } | ||||
|         return when (val tvType = if (episodes.isEmpty()) TvType.Movie else TvType.TvSeries) { | ||||
|  |  | |||
|  | @ -127,7 +127,7 @@ class PelisflixProvider : MainAPI() { | |||
|             } | ||||
|             if (list.isEmpty()) throw ErrorLoadingException("No Seasons Found") | ||||
| 
 | ||||
|             val episodeList = ArrayList<TvSeriesEpisode>() | ||||
|             val episodeList = ArrayList<Episode>() | ||||
| 
 | ||||
|             for ((seasonInt, seasonUrl) in list) { | ||||
|                 val seasonDocument = app.get(seasonUrl).document | ||||
|  | @ -141,14 +141,13 @@ class PelisflixProvider : MainAPI() { | |||
|                         val href = aName.attr("href") | ||||
|                         val date = episode.selectFirst("> td.MvTbTtl > span")?.text() | ||||
|                         episodeList.add( | ||||
|                             TvSeriesEpisode( | ||||
|                                 name, | ||||
|                                 seasonInt, | ||||
|                                 epNum, | ||||
|                                 href, | ||||
|                                 fixUrlNull(epthumb), | ||||
|                                 date | ||||
|                             ) | ||||
|                             newEpisode(href) { | ||||
|                                 this.name = name | ||||
|                                 this.season = seasonInt | ||||
|                                 this.episode =  epNum | ||||
|                                 this.posterUrl = fixUrlNull(epthumb) | ||||
|                                 addDate(date) | ||||
|                             } | ||||
|                         ) | ||||
|                     } | ||||
|                 } | ||||
|  |  | |||
|  | @ -111,11 +111,11 @@ class PelisplusHDProvider:MainAPI() { | |||
|             val isValid = seasonid.size == 2 | ||||
|             val episode = if (isValid) seasonid.getOrNull(1) else null | ||||
|             val season = if (isValid) seasonid.getOrNull(0) else null | ||||
|             TvSeriesEpisode( | ||||
|             Episode( | ||||
|                 href, | ||||
|                 name, | ||||
|                 season, | ||||
|                 episode, | ||||
|                 href, | ||||
|             ) | ||||
|         } | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,9 +1,13 @@ | |||
| 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.M3u8Helper | ||||
| import com.lagradost.cloudstream3.utils.getQualityFromName | ||||
| import com.lagradost.cloudstream3.utils.loadExtractor | ||||
| import org.jsoup.Jsoup | ||||
| 
 | ||||
| 
 | ||||
| /** Needs to inherit from MainAPI() to | ||||
|  * make the app know what functions to call | ||||
|  */ | ||||
|  | @ -74,6 +78,7 @@ open class PelisplusProviderTemplate : MainAPI() { | |||
|         val description = soup.selectFirst(".post-entry")?.text()?.trim() | ||||
|         val poster = soup.selectFirst("head meta[property=og:image]").attr("content") | ||||
| 
 | ||||
|         var year : Int? = null | ||||
|         val episodes = soup.select(".listing.items.lists > .video-block").map { li -> | ||||
|             val href = fixUrl(li.selectFirst("a").attr("href")) | ||||
|             val regexseason = Regex("(-[Tt]emporada-(\\d+)-[Cc]apitulo-(\\d+))") | ||||
|  | @ -87,20 +92,18 @@ open class PelisplusProviderTemplate : MainAPI() { | |||
|             val epThumb = fixUrl(li.selectFirst("img").attr("src")) | ||||
|             val epDate = li.selectFirst(".meta > .date").text() | ||||
| 
 | ||||
|             if(year == null) { | ||||
|                 year = epDate?.split("-")?.get(0)?.toIntOrNull() | ||||
|             } | ||||
| 
 | ||||
| 
 | ||||
|             TvSeriesEpisode( | ||||
|                 null, | ||||
|                 season, | ||||
|                 episode, | ||||
|                 fixUrl(li.selectFirst("a").attr("href")), | ||||
|                 epThumb, | ||||
|                 epDate | ||||
|             ) | ||||
|             newEpisode(li.selectFirst("a").attr("href")) { | ||||
|                 this.season = season | ||||
|                 this.episode = episode | ||||
|                 this.posterUrl = epThumb | ||||
|                 addDate(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 | ||||
| 
 | ||||
|  | @ -224,7 +227,7 @@ open class PelisplusProviderTemplate : MainAPI() { | |||
|     } | ||||
| 
 | ||||
|     // loadLinks gets the raw .mp4 or .m3u8 urls from the data parameter in the episodes class generated in load() | ||||
|     // See TvSeriesEpisode(...) in this provider. | ||||
|     // See Episode(...) in this provider. | ||||
|     // The data are usually links, but can be any other string to help aid loading the links. | ||||
|     override suspend fun loadLinks( | ||||
|         data: String, | ||||
|  |  | |||
|  | @ -138,7 +138,7 @@ class PinoyHDXyzProvider : MainAPI() { | |||
|         } | ||||
| 
 | ||||
|         // Try looking for episodes, for series | ||||
|         val episodeList = ArrayList<TvSeriesEpisode>() | ||||
|         val episodeList = ArrayList<Episode>() | ||||
|         val bodyText = body?.select("div.section-cotent1.col-md-12")?.select("section") | ||||
|             ?.select("script")?.toString() ?: "" | ||||
|         //Log.i(this.name, "Result => (bodyText) ${bodyText}") | ||||
|  | @ -151,7 +151,7 @@ class PinoyHDXyzProvider : MainAPI() { | |||
|                     val listEpStream = listOf(ep.trim()).toJson() | ||||
|                     //Log.i(this.name, "Result => (ep $count) $listEpStream") | ||||
|                     episodeList.add( | ||||
|                         TvSeriesEpisode( | ||||
|                         Episode( | ||||
|                             name = null, | ||||
|                             season = null, | ||||
|                             episode = count, | ||||
|  |  | |||
|  | @ -159,7 +159,7 @@ class PinoyMoviePediaProvider : MainAPI() { | |||
| 
 | ||||
|         // Parse episodes if series | ||||
|         if (isTvSeries) { | ||||
|             val episodeList = ArrayList<TvSeriesEpisode>() | ||||
|             val episodeList = ArrayList<Episode>() | ||||
|             val epLinks = playcontainer?.select("div > div > div.source-box") | ||||
|             //Log.i(this.name, "Result => (epList) ${epList}") | ||||
|             body?.select("div#playeroptions > ul > li")?.forEach { ep -> | ||||
|  | @ -175,7 +175,7 @@ class PinoyMoviePediaProvider : MainAPI() { | |||
|                     val streamEpLink = listOf(href.trim()).toJson() | ||||
|                     //Log.i(this.name, "Result => (streamEpLink $epNum) $streamEpLink") | ||||
|                     episodeList.add( | ||||
|                         TvSeriesEpisode( | ||||
|                         Episode( | ||||
|                             name = null, | ||||
|                             season = null, | ||||
|                             episode = epNum, | ||||
|  |  | |||
|  | @ -124,7 +124,7 @@ class SeriesflixProvider : MainAPI() { | |||
|             } | ||||
|             if (list.isEmpty()) throw ErrorLoadingException("No Seasons Found") | ||||
| 
 | ||||
|             val episodeList = ArrayList<TvSeriesEpisode>() | ||||
|             val episodeList = ArrayList<Episode>() | ||||
| 
 | ||||
|             for (season in list) { | ||||
|                 val seasonDocument = app.get(season.second).document | ||||
|  | @ -138,14 +138,13 @@ class SeriesflixProvider : MainAPI() { | |||
|                         val href = aName.attr("href") | ||||
|                         val date = episode.selectFirst("> td.MvTbTtl > span")?.text() | ||||
|                         episodeList.add( | ||||
|                             TvSeriesEpisode( | ||||
|                                 name, | ||||
|                                 season.first, | ||||
|                                 epNum, | ||||
|                                 href, | ||||
|                                 fixUrlNull(epthumb), | ||||
|                                 date | ||||
|                             ) | ||||
|                             newEpisode(href) { | ||||
|                                 this.name = name | ||||
|                                 this.season = season.first | ||||
|                                 this.episode = epNum | ||||
|                                 this.posterUrl = fixUrlNull(epthumb) | ||||
|                                 addDate(date) | ||||
|                             } | ||||
|                         ) | ||||
|                     } | ||||
|                 } | ||||
|  |  | |||
|  | @ -11,7 +11,6 @@ import com.lagradost.cloudstream3.animeproviders.ZoroProvider | |||
| import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall | ||||
| import com.lagradost.cloudstream3.network.AppResponse | ||||
| import com.lagradost.cloudstream3.utils.AppUtils.parseJson | ||||
| import com.lagradost.cloudstream3.utils.AppUtils.toJson | ||||
| import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson | ||||
| import com.lagradost.cloudstream3.utils.Coroutines.ioSafe | ||||
| import com.lagradost.cloudstream3.utils.ExtractorLink | ||||
|  | @ -214,7 +213,7 @@ open class SflixProvider : MainAPI() { | |||
| 
 | ||||
|             val comingSoon = sourceIds.isEmpty() | ||||
| 
 | ||||
|             return newMovieLoadResponse(title, url, TvType.Movie, sourceIds.toJson()) { | ||||
|             return newMovieLoadResponse(title, url, TvType.Movie, sourceIds) { | ||||
|                 this.year = year | ||||
|                 this.posterUrl = posterUrl | ||||
|                 this.plot = plot | ||||
|  | @ -228,7 +227,7 @@ open class SflixProvider : MainAPI() { | |||
|             } | ||||
|         } else { | ||||
|             val seasonsDocument = app.get("$mainUrl/ajax/v2/tv/seasons/$id").document | ||||
|             val episodes = arrayListOf<TvSeriesEpisode>() | ||||
|             val episodes = arrayListOf<Episode>() | ||||
|             var seasonItems = seasonsDocument.select("div.dropdown-menu.dropdown-menu-model > a") | ||||
|             if (seasonItems.isNullOrEmpty()) | ||||
|                 seasonItems = seasonsDocument.select("div.dropdown-menu > a.dropdown-item") | ||||
|  | @ -260,13 +259,12 @@ open class SflixProvider : MainAPI() { | |||
|                         } ?: episode | ||||
| 
 | ||||
|                     episodes.add( | ||||
|                         TvSeriesEpisode( | ||||
|                             episodeTitle?.removePrefix("Episode $episodeNum: "), | ||||
|                             season + 1, | ||||
|                             episodeNum, | ||||
|                             Pair(url, episodeData).toJson(), | ||||
|                             fixUrlNull(episodePosterUrl) | ||||
|                         ) | ||||
|                         newEpisode(Pair(url, episodeData)) { | ||||
|                             this.posterUrl = fixUrlNull(episodePosterUrl) | ||||
|                             this.name = episodeTitle?.removePrefix("Episode $episodeNum: ") | ||||
|                             this.season = season + 1 | ||||
|                             this.episode = episodeNum | ||||
|                         } | ||||
|                     ) | ||||
|                 } | ||||
|             } | ||||
|  | @ -370,7 +368,8 @@ open class SflixProvider : MainAPI() { | |||
|         val posterUrl = img.attr("data-src") ?: img.attr("src") | ||||
|         val href = fixUrl(inner.select("a").attr("href")) | ||||
|         val isMovie = href.contains("/movie/") | ||||
|         val otherInfo = this.selectFirst("div.film-detail > div.fd-infor")?.select("span")?.toList() ?: listOf() | ||||
|         val otherInfo = | ||||
|             this.selectFirst("div.film-detail > div.fd-infor")?.select("span")?.toList() ?: listOf() | ||||
|         //var rating: Int? = null | ||||
|         var year: Int? = null | ||||
|         var quality: SearchQuality? = null | ||||
|  |  | |||
|  | @ -75,7 +75,7 @@ class SoaptwoDayProvider:MainAPI() { | |||
|         val episodes = soup.select("div.alert > div > div > a").mapNotNull { | ||||
|             val link = fixUrlNull(it?.attr("href")) ?: return@mapNotNull null | ||||
|             val name = it?.text()?.replace(Regex("(^(\\d+)\\.)"),"") | ||||
|             TvSeriesEpisode( | ||||
|             Episode( | ||||
|                 name = name, | ||||
|                 data = link | ||||
|             ) | ||||
|  |  | |||
|  | @ -420,13 +420,15 @@ class TheFlixToProvider : MainAPI() { | |||
|                 ) | ||||
|                 //{"affiliateCode":"","pathname":"/movie/696806-the-adam-project"} | ||||
|                 val data = mapOf("affiliateCode" to "", "pathname" to url.removePrefix(mainUrl)) | ||||
|                 val resp = app.post(optionsUrl, headers = mapOf( | ||||
|                     "User-Agent" to USER_AGENT, | ||||
|                     "Content-Type" to "application/json;charset=UTF-8", | ||||
|                     "Accept" to "application/json, text/plain, */*", | ||||
|                     "Origin" to url, | ||||
|                     "Referer" to mainUrl, | ||||
|                 ), data = data) | ||||
|                 val resp = app.post( | ||||
|                     optionsUrl, headers = mapOf( | ||||
|                         "User-Agent" to USER_AGENT, | ||||
|                         "Content-Type" to "application/json;charset=UTF-8", | ||||
|                         "Accept" to "application/json, text/plain, */*", | ||||
|                         "Origin" to url, | ||||
|                         "Referer" to mainUrl, | ||||
|                     ), data = data | ||||
|                 ) | ||||
| 
 | ||||
|                 latestCookies = resp.cookies | ||||
|                 val newData = getLoadMan(url) | ||||
|  | @ -442,7 +444,7 @@ class TheFlixToProvider : MainAPI() { | |||
|     override suspend fun load(url: String): LoadResponse? { | ||||
|         val tvtype = if (url.contains("movie")) TvType.Movie else TvType.TvSeries | ||||
|         val json = getLoadMainRetry(url) | ||||
|         val episodes = ArrayList<TvSeriesEpisode>() | ||||
|         val episodes = ArrayList<Episode>() | ||||
|         val isMovie = tvtype == TvType.Movie | ||||
|         val pageMain = json.props.pageProps | ||||
| 
 | ||||
|  | @ -471,11 +473,11 @@ class TheFlixToProvider : MainAPI() { | |||
|                     val test = epi.videos | ||||
|                     val ratinginfo = (epi.voteAverage)?.times(10)?.toInt() | ||||
|                     val rating = if (ratinginfo?.equals(0) == true) null else ratinginfo | ||||
|                     val eps = TvSeriesEpisode( | ||||
|                     val eps = Episode( | ||||
|                         "$mainUrl/tv-show/$movieId-${cleanTitle(movietitle)}/season-$seasonum/episode-$episodenu", | ||||
|                         title, | ||||
|                         seasonum, | ||||
|                         episodenu, | ||||
|                         "$mainUrl/tv-show/$movieId-${cleanTitle(movietitle)}/season-$seasonum/episode-$episodenu", | ||||
|                         description = epDesc!!, | ||||
|                         posterUrl = seasonPoster, | ||||
|                         rating = rating, | ||||
|  |  | |||
|  | @ -28,7 +28,8 @@ class VfSerieProvider : MainAPI() { | |||
|         for (item in items) { | ||||
|             val href = item.attr("href") | ||||
| 
 | ||||
|             val poster = item.selectFirst("> div.Image > figure > img").attr("src").replace("//image", "https://image") | ||||
|             val poster = item.selectFirst("> div.Image > figure > img").attr("src") | ||||
|                 .replace("//image", "https://image") | ||||
| 
 | ||||
|             if (poster == "$mainUrl/wp-content/themes/toroplay/img/cnt/noimg-thumbnail.png") {  // if the poster is missing (the item is just a redirect to something like https://vf-serie.org/series-tv/) | ||||
|                 continue | ||||
|  | @ -37,7 +38,17 @@ class VfSerieProvider : MainAPI() { | |||
| 
 | ||||
|             val year = item.selectFirst("> span.Year").text()?.toIntOrNull() | ||||
| 
 | ||||
|             returnValue.add(TvSeriesSearchResponse(name, href, this.name, TvType.TvSeries, poster, year, null)) | ||||
|             returnValue.add( | ||||
|                 TvSeriesSearchResponse( | ||||
|                     name, | ||||
|                     href, | ||||
|                     this.name, | ||||
|                     TvType.TvSeries, | ||||
|                     poster, | ||||
|                     year, | ||||
|                     null | ||||
|                 ) | ||||
|             ) | ||||
|         } | ||||
|         return returnValue | ||||
|     } | ||||
|  | @ -99,7 +110,8 @@ class VfSerieProvider : MainAPI() { | |||
|         val response = app.get(url).text | ||||
|         val document = Jsoup.parse(response) | ||||
|         val title = | ||||
|             document?.selectFirst(".Title")?.text()?.replace("Regarder Serie ", "")?.replace(" En Streaming", "") | ||||
|             document?.selectFirst(".Title")?.text()?.replace("Regarder Serie ", "") | ||||
|                 ?.replace(" En Streaming", "") | ||||
|                 ?: throw ErrorLoadingException("Service might be unavailable") | ||||
| 
 | ||||
| 
 | ||||
|  | @ -109,7 +121,8 @@ class VfSerieProvider : MainAPI() { | |||
|         //val duration = document.select("span.Time").text()?.toIntOrNull() | ||||
| 
 | ||||
|         val backgroundPoster = | ||||
|             document.selectFirst("div.Image > figure > img").attr("src").replace("//image", "https://image") | ||||
|             document.selectFirst("div.Image > figure > img").attr("src") | ||||
|                 .replace("//image", "https://image") | ||||
| 
 | ||||
|         val descript = document.selectFirst("div.Description > p").text() | ||||
| 
 | ||||
|  | @ -124,7 +137,7 @@ class VfSerieProvider : MainAPI() { | |||
|         } | ||||
|         if (list.isEmpty()) throw ErrorLoadingException("No Seasons Found") | ||||
| 
 | ||||
|         val episodeList = ArrayList<TvSeriesEpisode>() | ||||
|         val episodeList = ArrayList<Episode>() | ||||
| 
 | ||||
|         for (season in list) { | ||||
|             val episodes = document.select("table > tbody > tr") | ||||
|  | @ -132,20 +145,20 @@ class VfSerieProvider : MainAPI() { | |||
|                 episodes.forEach { episode -> | ||||
|                     val epNum = episode.selectFirst("> span.Num")?.text()?.toIntOrNull() | ||||
|                     val poster = | ||||
|                         episode.selectFirst("> td.MvTbImg > a > img")?.attr("src")?.replace("//image", "https://image") | ||||
|                         episode.selectFirst("> td.MvTbImg > a > img")?.attr("src") | ||||
|                             ?.replace("//image", "https://image") | ||||
|                     val aName = episode.selectFirst("> td.MvTbTtl > a") | ||||
|                     val date = episode.selectFirst("> td.MvTbTtl > span")?.text()?.toString() | ||||
|                     val name = aName.text() | ||||
|                     val href = aName.attr("href") | ||||
|                     episodeList.add( | ||||
|                         TvSeriesEpisode( | ||||
|                             name, | ||||
|                             season, | ||||
|                             epNum, | ||||
|                             href, | ||||
|                             poster, | ||||
|                             date | ||||
|                         ) | ||||
|                         newEpisode(href) { | ||||
|                             this.name = name | ||||
|                             this.season = season | ||||
|                             this.episode = epNum | ||||
|                             this.posterUrl = poster | ||||
|                             addDate(date) | ||||
|                         } | ||||
|                     ) | ||||
|                 } | ||||
|             } | ||||
|  |  | |||
|  | @ -85,6 +85,7 @@ open class VidstreamProviderTemplate : MainAPI() { | |||
| 
 | ||||
|         val description = soup.selectFirst(".post-entry")?.text()?.trim() | ||||
|         var poster: String? = null | ||||
|         var year : Int? = null | ||||
| 
 | ||||
|         val episodes = | ||||
|             soup.select(".listing.items.lists > .video-block").withIndex().map { (_, li) -> | ||||
|  | @ -104,19 +105,16 @@ open class VidstreamProviderTemplate : MainAPI() { | |||
| 
 | ||||
|                 val epNum = Regex("""Episode (\d+)""").find(epTitle)?.destructured?.component1() | ||||
|                     ?.toIntOrNull() | ||||
| 
 | ||||
|                 TvSeriesEpisode( | ||||
|                     epTitle, | ||||
|                     null, | ||||
|                     epNum, | ||||
|                     fixUrl(li.selectFirst("a").attr("href")), | ||||
|                     epThumb, | ||||
|                     epDate | ||||
|                 ) | ||||
|                 if(year == null) { | ||||
|                     year = epDate?.split("-")?.get(0)?.toIntOrNull() | ||||
|                 } | ||||
|                 newEpisode(li.selectFirst("a").attr("href")) { | ||||
|                     this.episode = epNum | ||||
|                     this.posterUrl = epThumb | ||||
|                     addDate(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 | ||||
|  | @ -208,7 +206,7 @@ open class VidstreamProviderTemplate : MainAPI() { | |||
|     } | ||||
| 
 | ||||
|     // loadLinks gets the raw .mp4 or .m3u8 urls from the data parameter in the episodes class generated in load() | ||||
|     // See TvSeriesEpisode(...) in this provider. | ||||
|     // See Episode(...) in this provider. | ||||
|     // The data are usually links, but can be any other string to help aid loading the links. | ||||
|     override suspend fun loadLinks( | ||||
|         data: String, | ||||
|  |  | |||
|  | @ -156,7 +156,7 @@ class WatchAsianProvider : MainAPI() { | |||
|             val regex = "(?<=episode-).*?(?=.html)".toRegex() | ||||
|             val count = regex.find(epLink, mainUrl.length)?.value?.toIntOrNull() ?: 0 | ||||
|             //Log.i(this.name, "Result => $epLink (regexYear) ${count}") | ||||
|             TvSeriesEpisode( | ||||
|             Episode( | ||||
|                 name = null, | ||||
|                 season = null, | ||||
|                 episode = count, | ||||
|  |  | |||
|  | @ -112,11 +112,11 @@ class FrenchStreamProvider : MainAPI() { | |||
|                 if (poster == null) { | ||||
|                     poster = a.selectFirst("div.fposter > img")?.attr("src") | ||||
|                 } | ||||
|                 TvSeriesEpisode( | ||||
|                 Episode( | ||||
|                     fixUrl(url).plus("-episodenumber:$epNum"), | ||||
|                     epTitle, | ||||
|                     null, | ||||
|                     epNum, | ||||
|                     fixUrl(url).plus("-episodenumber:$epNum"), | ||||
|                     null,  // episode Thumbnail | ||||
|                     null // episode date | ||||
|                 ) | ||||
|  |  | |||
|  | @ -364,8 +364,8 @@ class ResultViewModel : ViewModel() { | |||
|                                     filterName(i.name), | ||||
|                                     i.posterUrl, | ||||
|                                     episode, | ||||
|                                     null, // TODO FIX SEASON | ||||
|                                     i.url, | ||||
|                                     i.season, | ||||
|                                     i.data, | ||||
|                                     apiName, | ||||
|                                     mainId + index + 1 + idIndex * 100000, | ||||
|                                     index, | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue