api refactor, breaking change for forks

This commit is contained in:
LagradOst 2022-04-09 16:50:04 +02:00
parent 99fa40eaeb
commit 750b1878cb
49 changed files with 564 additions and 376 deletions

View file

@ -36,7 +36,7 @@ android {
targetSdkVersion 30 targetSdkVersion 30
versionCode 45 versionCode 45
versionName "2.9.17" versionName "2.9.18"
resValue "string", "app_version", resValue "string", "app_version",
"${defaultConfig.versionName}${versionNameSuffix ?: ""}" "${defaultConfig.versionName}${versionNameSuffix ?: ""}"

View file

@ -13,11 +13,14 @@ import com.fasterxml.jackson.module.kotlin.KotlinModule
import com.lagradost.cloudstream3.animeproviders.* import com.lagradost.cloudstream3.animeproviders.*
import com.lagradost.cloudstream3.metaproviders.CrossTmdbProvider import com.lagradost.cloudstream3.metaproviders.CrossTmdbProvider
import com.lagradost.cloudstream3.movieproviders.* 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.aniListApi
import com.lagradost.cloudstream3.syncproviders.OAuth2API.Companion.malApi import com.lagradost.cloudstream3.syncproviders.OAuth2API.Companion.malApi
import com.lagradost.cloudstream3.ui.player.SubtitleData import com.lagradost.cloudstream3.ui.player.SubtitleData
import com.lagradost.cloudstream3.utils.AppUtils.toJson
import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.ExtractorLink
import okhttp3.Interceptor import okhttp3.Interceptor
import java.text.SimpleDateFormat
import java.util.* import java.util.*
import kotlin.math.absoluteValue import kotlin.math.absoluteValue
@ -800,16 +803,6 @@ fun TvType?.isEpisodeBased(): Boolean {
return (this == TvType.TvSeries || this == TvType.Anime) 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( data class TorrentLoadResponse(
override var name: String, override var name: String,
override var url: String, override var url: String,
@ -841,7 +834,7 @@ data class AnimeLoadResponse(
override var posterUrl: String? = null, override var posterUrl: String? = null,
override var year: Int? = null, override var year: Int? = null,
var episodes: MutableMap<DubStatus, List<AnimeEpisode>> = mutableMapOf(), var episodes: MutableMap<DubStatus, List<Episode>> = mutableMapOf(),
var showStatus: ShowStatus? = null, var showStatus: ShowStatus? = null,
override var plot: String? = null, override var plot: String? = null,
@ -857,7 +850,7 @@ data class AnimeLoadResponse(
override var syncData: MutableMap<String, String> = mutableMapOf(), override var syncData: MutableMap<String, String> = mutableMapOf(),
) : LoadResponse ) : LoadResponse
fun AnimeLoadResponse.addEpisodes(status: DubStatus, episodes: List<AnimeEpisode>?) { fun AnimeLoadResponse.addEpisodes(status: DubStatus, episodes: List<Episode>?) {
if (episodes == null) return if (episodes == null) return
this.episodes[status] = episodes this.episodes[status] = episodes
} }
@ -912,6 +905,26 @@ data class MovieLoadResponse(
override var syncData: MutableMap<String, String> = mutableMapOf(), override var syncData: MutableMap<String, String> = mutableMapOf(),
) : LoadResponse ) : 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( fun MainAPI.newMovieLoadResponse(
name: String, name: String,
url: String, url: String,
@ -931,23 +944,58 @@ fun MainAPI.newMovieLoadResponse(
return builder return builder
} }
data class TvSeriesEpisode( data class Episode(
val name: String? = null, var data: String,
val season: Int? = null, var name: String? = null,
val episode: Int? = null, var season: Int? = null,
val data: String, var episode: Int? = null,
val posterUrl: String? = null, var posterUrl: String? = null,
val date: String? = null, var rating: Int? = null,
val rating: Int? = null, var description: String? = null,
val 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( data class TvSeriesLoadResponse(
override var name: String, override var name: String,
override var url: String, override var url: String,
override var apiName: String, override var apiName: String,
override var type: TvType, override var type: TvType,
var episodes: List<TvSeriesEpisode>, var episodes: List<Episode>,
override var posterUrl: String? = null, override var posterUrl: String? = null,
override var year: Int? = null, override var year: Int? = null,
@ -968,7 +1016,7 @@ fun MainAPI.newTvSeriesLoadResponse(
name: String, name: String,
url: String, url: String,
type: TvType, type: TvType,
episodes: List<TvSeriesEpisode>, episodes: List<Episode>,
initializer: TvSeriesLoadResponse.() -> Unit = { } initializer: TvSeriesLoadResponse.() -> Unit = { }
): TvSeriesLoadResponse { ): TvSeriesLoadResponse {
val builder = TvSeriesLoadResponse( val builder = TvSeriesLoadResponse(

View file

@ -232,11 +232,11 @@ class AllAnimeProvider : MainAPI() {
val episodes = showData.availableEpisodes.let { val episodes = showData.availableEpisodes.let {
if (it == null) return@let Pair(null, null) if (it == null) return@let Pair(null, null)
Pair(if (it.sub != 0) ((1..it.sub).map { epNum -> Pair(if (it.sub != 0) ((1..it.sub).map { epNum ->
AnimeEpisode( Episode(
"$mainUrl/anime/${showData.Id}/episodes/sub/$epNum", episode = epNum "$mainUrl/anime/${showData.Id}/episodes/sub/$epNum", episode = epNum
) )
}) else null, if (it.dub != 0) ((1..it.dub).map { epNum -> }) else null, if (it.dub != 0) ((1..it.dub).map { epNum ->
AnimeEpisode( Episode(
"$mainUrl/anime/${showData.Id}/episodes/dub/$epNum", episode = epNum "$mainUrl/anime/${showData.Id}/episodes/dub/$epNum", episode = epNum
) )
}) else null) }) else null)

View file

@ -65,7 +65,7 @@ class AnimeFlickProvider : MainAPI() {
val episodes = doc.select("#collapseOne .block-space > .row > div:nth-child(2)").map { val episodes = doc.select("#collapseOne .block-space > .row > div:nth-child(2)").map {
val name = it.selectFirst("a").text() val name = it.selectFirst("a").text()
val link = mainUrl + it.selectFirst("a").attr("href") val link = mainUrl + it.selectFirst("a").attr("href")
AnimeEpisode(link, name) Episode(link, name)
}.reversed() }.reversed()
return newAnimeLoadResponse(title, url, getType(title)) { return newAnimeLoadResponse(title, url, getType(title)) {

View file

@ -189,7 +189,7 @@ class AnimePaheProvider : MainAPI() {
) )
private suspend fun generateListOfEpisodes(link: String): ArrayList<AnimeEpisode> { private suspend fun generateListOfEpisodes(link: String): ArrayList<Episode> {
try { try {
val attrs = link.split('/') val attrs = link.split('/')
val id = attrs[attrs.size - 1].split("?")[0] val id = attrs[attrs.size - 1].split("?")[0]
@ -204,7 +204,7 @@ class AnimePaheProvider : MainAPI() {
val perPage = data.perPage val perPage = data.perPage
val total = data.total val total = data.total
var ep = 1 var ep = 1
val episodes = ArrayList<AnimeEpisode>() val episodes = ArrayList<Episode>()
fun getEpisodeTitle(k: AnimeData): String { fun getEpisodeTitle(k: AnimeData): String {
return k.title.ifEmpty { return k.title.ifEmpty {
@ -215,14 +215,11 @@ class AnimePaheProvider : MainAPI() {
if (lastPage == 1 && perPage > total) { if (lastPage == 1 && perPage > total) {
data.data.forEach { data.data.forEach {
episodes.add( episodes.add(
AnimeEpisode( newEpisode("$mainUrl/api?m=links&id=${it.animeId}&session=${it.session}&p=kwik!!TRUE!!") {
"$mainUrl/api?m=links&id=${it.animeId}&session=${it.session}&p=kwik!!TRUE!!", addDate(it.createdAt)
getEpisodeTitle(it), this.name = getEpisodeTitle(it)
it.snapshot.ifEmpty { this.posterUrl = it.snapshot
null }
},
it.createdAt
)
) )
} }
} else { } else {
@ -230,7 +227,7 @@ class AnimePaheProvider : MainAPI() {
for (i in 0 until perPage) { for (i in 0 until perPage) {
if (ep <= total) { if (ep <= total) {
episodes.add( episodes.add(
AnimeEpisode( Episode(
"$mainUrl/api?m=release&id=${id}&sort=episode_asc&page=${page + 1}&ep=${ep}!!FALSE!!" "$mainUrl/api?m=release&id=${id}&sort=episode_asc&page=${page + 1}&ep=${ep}!!FALSE!!"
) )
) )

View file

@ -152,7 +152,7 @@ class AnimeWorldProvider : MainAPI() {
val episodes = servers.select(".server[data-name=\"9\"] .episode").map { val episodes = servers.select(".server[data-name=\"9\"] .episode").map {
val id = it.select("a").attr("data-id") val id = it.select("a").attr("data-id")
val number = it.select("a").attr("data-episode-num").toIntOrNull() val number = it.select("a").attr("data-episode-num").toIntOrNull()
AnimeEpisode( Episode(
fixUrl("$mainUrl/api/episode/info?id=$id"), fixUrl("$mainUrl/api/episode/info?id=$id"),
episode = number episode = number
) )

View file

@ -115,7 +115,7 @@ class AnimeflvnetProvider:MainAPI() {
override suspend fun load(url: String): LoadResponse { override suspend fun load(url: String): LoadResponse {
val doc = app.get(url).document val doc = app.get(url).document
val episodes = ArrayList<AnimeEpisode>() val episodes = ArrayList<Episode>()
val title = doc.selectFirst("h1.Title").text() val title = doc.selectFirst("h1.Title").text()
val poster = doc.selectFirst("div.AnimeCover div.Image figure img").attr("src") val poster = doc.selectFirst("div.AnimeCover div.Image figure img").attr("src")
val description = doc.selectFirst("div.Description p").text() 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 animeid = doc.selectFirst("div.Strs.RateIt").attr("data-id")
val epthumb = "https://cdn.animeflv.net/screenshots/$animeid/$epNum/th_3.jpg" val epthumb = "https://cdn.animeflv.net/screenshots/$animeid/$epNum/th_3.jpg"
val link = url.replace("/anime/","/ver/")+"-$epNum" val link = url.replace("/anime/","/ver/")+"-$epNum"
episodes.add( AnimeEpisode( episodes.add( Episode(
link, link,
null, null,
posterUrl = epthumb, posterUrl = epthumb,

View file

@ -106,7 +106,7 @@ class AnimekisaProvider : MainAPI() {
) ShowStatus.Ongoing else ShowStatus.Completed ) ShowStatus.Ongoing else ShowStatus.Completed
val episodes = doc.select("div.tab-content ul li.nav-item").map { val episodes = doc.select("div.tab-content ul li.nav-item").map {
val link = it.selectFirst("a").attr("href") val link = it.selectFirst("a").attr("href")
AnimeEpisode(link) Episode(link)
} }
val type = if (doc.selectFirst(".dp-i-stats").toString() val type = if (doc.selectFirst(".dp-i-stats").toString()
.contains("Movies") .contains("Movies")

View file

@ -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 = val url =
mainUrl + (if (isMovie) "/movies/jsonMovie" else "/xz/v3/jsonEpi") + ".php?slug=$slug&_=$unixTime" mainUrl + (if (isMovie) "/movies/jsonMovie" else "/xz/v3/jsonEpi") + ".php?slug=$slug&_=$unixTime"
val response = app.get(url).text val response = app.get(url).text
@ -196,7 +196,7 @@ class DubbedAnimeProvider : MainAPI() {
): Boolean { ): Boolean {
val serversHTML = (if (data.startsWith(mainUrl)) { // CLASSIC EPISODE val serversHTML = (if (data.startsWith(mainUrl)) { // CLASSIC EPISODE
val slug = getSlug(data) val slug = getSlug(data)
getAnimeEpisode(slug, false).serversHTML getEpisode(slug, false).serversHTML
} else data).replace("\\", "") } else data).replace("\\", "")
val hls = ArrayList("hl=\"(.*?)\"".toRegex().findAll(serversHTML).map { val hls = ArrayList("hl=\"(.*?)\"".toRegex().findAll(serversHTML).map {
@ -228,7 +228,7 @@ class DubbedAnimeProvider : MainAPI() {
override suspend fun load(url: String): LoadResponse { override suspend fun load(url: String): LoadResponse {
if (getIsMovie(url)) { if (getIsMovie(url)) {
val realSlug = url.replace("movies/", "") val realSlug = url.replace("movies/", "")
val episode = getAnimeEpisode(realSlug, true) val episode = getEpisode(realSlug, true)
val poster = episode.previewImg ?: episode.wideImg val poster = episode.previewImg ?: episode.wideImg
return MovieLoadResponse( return MovieLoadResponse(
episode.title, episode.title,
@ -253,7 +253,7 @@ class DubbedAnimeProvider : MainAPI() {
val episodes = document.select("a.epibloks").map { val episodes = document.select("a.epibloks").map {
val epTitle = it.selectFirst("> div.inwel > span.isgrxx")?.text() 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")) val img = fixUrl(document.select("div.fkimgs > img").attr("src"))

View file

@ -303,7 +303,7 @@ class GogoanimeProvider : MainAPI() {
val params = mapOf("ep_start" to "0", "ep_end" to "2000", "id" to animeId) 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 { val episodes = app.get(episodeloadApi, params = params).document.select("a").map {
AnimeEpisode( Episode(
fixUrl(it.attr("href").trim()), fixUrl(it.attr("href").trim()),
"Episode " + it.selectFirst(".name").text().replace("EP", "").trim() "Episode " + it.selectFirst(".name").text().replace("EP", "").trim()
) )

View file

@ -94,7 +94,7 @@ class KawaiifuProvider : MainAPI() {
val episodes = Jsoup.parse( val episodes = Jsoup.parse(
app.get(episodesLink).text app.get(episodesLink).text
).selectFirst(".list-ep").select("li").map { ).selectFirst(".list-ep").select("li").map {
AnimeEpisode( Episode(
it.selectFirst("a").attr("href"), it.selectFirst("a").attr("href"),
if (it.text().trim().toIntOrNull() != null) "Episode ${it.text().trim()}" else it.text().trim() if (it.text().trim().toIntOrNull() != null) "Episode ${it.text().trim()}" else it.text().trim()
) )

View file

@ -5,7 +5,6 @@ import com.lagradost.cloudstream3.extractors.FEmbed
import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.loadExtractor import com.lagradost.cloudstream3.utils.loadExtractor
import java.util.* import java.util.*
import kotlin.collections.ArrayList
class MonoschinosProvider : MainAPI() { class MonoschinosProvider : MainAPI() {
@ -132,7 +131,7 @@ class MonoschinosProvider : MainAPI() {
val name = it.selectFirst("p.animetitles").text() val name = it.selectFirst("p.animetitles").text()
val link = it.selectFirst("a").attr("href") val link = it.selectFirst("a").attr("href")
val epThumb = it.selectFirst(".animeimghv").attr("data-src") val epThumb = it.selectFirst(".animeimghv").attr("data-src")
AnimeEpisode(link, name, posterUrl = epThumb) Episode(link, name, posterUrl = epThumb)
} }
return newAnimeLoadResponse(title, url, getType(type)) { return newAnimeLoadResponse(title, url, getType(type)) {
posterUrl = poster posterUrl = poster

View file

@ -203,7 +203,7 @@ class NineAnimeProvider : MainAPI() {
)?.select("ul.episodes li a")?.mapNotNull { )?.select("ul.episodes li a")?.mapNotNull {
val link = it?.attr("href") ?: return@mapNotNull null val link = it?.attr("href") ?: return@mapNotNull null
val name = "Episode ${it.text()}" val name = "Episode ${it.text()}"
AnimeEpisode(link, name) Episode(link, name)
} ?: return null } ?: return null

View file

@ -125,16 +125,15 @@ class TenshiProvider : MainAPI() {
} }
@SuppressLint("SimpleDateFormat") @SuppressLint("SimpleDateFormat")
private fun dateParser(dateString: String?): String? { private fun dateParser(dateString: String?): Date? {
if (dateString == null) return null if (dateString == null) return null
try { try {
val format = SimpleDateFormat("dd 'of' MMM',' yyyy") val format = SimpleDateFormat("dd 'of' MMM',' yyyy")
val newFormat = SimpleDateFormat("dd-MM-yyyy")
val data = format.parse( val data = format.parse(
dateString.replace("th ", " ").replace("st ", " ").replace("nd ", " ") dateString.replace("th ", " ").replace("st ", " ").replace("nd ", " ")
.replace("rd ", " ") .replace("rd ", " ")
) ?: return null ) ?: return null
return newFormat.format(data) return data
} catch (e: Exception) { } catch (e: Exception) {
return null return null
} }
@ -246,14 +245,12 @@ class TenshiProvider : MainAPI() {
val episodes = ArrayList(episodeNodes.map { val episodes = ArrayList(episodeNodes.map {
val title = it.selectFirst(".episode-title")?.text()?.trim() val title = it.selectFirst(".episode-title")?.text()?.trim()
AnimeEpisode( newEpisode(it.attr("href")) {
it.attr("href"), this.name = if (title == "No Title") null else title
if(title == "No Title") null else title, this.posterUrl = it.selectFirst("img")?.attr("src")
it.selectFirst("img")?.attr("src"), addDate(dateParser(it?.selectFirst(".episode-date")?.text()?.trim()))
dateParser(it?.selectFirst(".episode-date")?.text()?.trim()), this.description = it.attr("data-content").trim()
null, }
it.attr("data-content").trim(),
)
}) })
val similarAnime = document.select("ul.anime-loop > li > a")?.mapNotNull { element -> val similarAnime = document.select("ul.anime-loop > li > a")?.mapNotNull { element ->

View file

@ -113,28 +113,26 @@ class WatchCartoonOnlineProvider : MainAPI() {
val href = it.attr("href") val href = it.attr("href")
if (match != null) { if (match != null) {
val last = match.groupValues[3] val last = match.groupValues[3]
return@map TvSeriesEpisode( return@map Episode(
href,
if (last.startsWith("English")) null else last, if (last.startsWith("English")) null else last,
match.groupValues[1].toIntOrNull(), match.groupValues[1].toIntOrNull(),
match.groupValues[2].toIntOrNull(), match.groupValues[2].toIntOrNull(),
href
) )
} }
val match2 = Regex("Episode ([0-9]*).*? (.*)").find(text) val match2 = Regex("Episode ([0-9]*).*? (.*)").find(text)
if (match2 != null) { if (match2 != null) {
val last = match2.groupValues[2] val last = match2.groupValues[2]
return@map TvSeriesEpisode( return@map Episode(
href,
if (last.startsWith("English")) null else last, if (last.startsWith("English")) null else last,
null, null,
match2.groupValues[1].toIntOrNull(), match2.groupValues[1].toIntOrNull(),
href
) )
} }
return@map TvSeriesEpisode( return@map Episode(
text, href,
null, text
null,
href
) )
} }
TvSeriesLoadResponse( TvSeriesLoadResponse(
@ -162,7 +160,7 @@ class WatchCartoonOnlineProvider : MainAPI() {
url, url,
this.name, this.name,
TvType.TvSeries, TvType.TvSeries,
listOf(TvSeriesEpisode(title, null, null, url)), listOf(Episode(url,title)),
null, null,
null, null,
description, description,

View file

@ -181,7 +181,7 @@ class WcoProvider : MainAPI() {
val episodeNodes = document.select(".tab-content .nav-item > a") val episodeNodes = document.select(".tab-content .nav-item > a")
val episodes = ArrayList(episodeNodes?.map { val episodes = ArrayList(episodeNodes?.map {
AnimeEpisode(it.attr("href")) Episode(it.attr("href"))
} ?: ArrayList()) } ?: ArrayList())
val statusElem = val statusElem =

View file

@ -231,16 +231,10 @@ class ZoroProvider : MainAPI() {
).text ).text
).html ).html
).select(".ss-list > a[href].ssl-item.ep-item").map { ).select(".ss-list > a[href].ssl-item.ep-item").map {
val name = it?.attr("title") newEpisode(it.attr("href")) {
AnimeEpisode( this.name = it?.attr("title")
fixUrl(it.attr("href")), this.episode = it.selectFirst(".ssli-order")?.text()?.toIntOrNull()
name, }
null,
null,
null,
null,
it.selectFirst(".ssli-order")?.text()?.toIntOrNull()
)
} }
val actors = document.select("div.block-actors-content > div.bac-list-wrap > div.bac-item") val actors = document.select("div.block-actors-content > div.bac-list-wrap > div.bac-item")

View file

@ -94,23 +94,23 @@ open class TmdbProvider : MainAPI() {
val episodes = this.seasons?.filter { !disableSeasonZero || (it.season_number ?: 0) != 0 } val episodes = this.seasons?.filter { !disableSeasonZero || (it.season_number ?: 0) != 0 }
?.mapNotNull { season -> ?.mapNotNull { season ->
season.episodes?.map { episode -> season.episodes?.map { episode ->
TvSeriesEpisode( Episode(
episode.name,
episode.season_number,
episode.episode_number,
TmdbLink( TmdbLink(
episode.external_ids?.imdb_id ?: this.external_ids?.imdb_id, episode.external_ids?.imdb_id ?: this.external_ids?.imdb_id,
this.id, this.id,
episode.episode_number, episode.episode_number,
episode.season_number, episode.season_number,
).toJson(), ).toJson(),
episode.name,
episode.season_number,
episode.episode_number,
getImageUrl(episode.still_path), getImageUrl(episode.still_path),
episode.air_date?.toString(),
episode.rating, episode.rating,
episode.overview, episode.overview,
episode.air_date?.time,
) )
} ?: (1..(season.episode_count ?: 1)).map { episodeNum -> } ?: (1..(season.episode_count ?: 1)).map { episodeNum ->
TvSeriesEpisode( Episode(
episode = episodeNum, episode = episodeNum,
data = TmdbLink( data = TmdbLink(
this.external_ids?.imdb_id, this.external_ids?.imdb_id,

View file

@ -63,20 +63,18 @@ class AkwamProvider : MainAPI() {
return Regex("""\d+""").find(this)?.groupValues?.firstOrNull()?.toIntOrNull() 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 a = select("a.text-white")
val url = a.attr("href") val url = a.attr("href")
val title = a.text() val title = a.text()
val thumbUrl = select("picture > img").attr("src") val thumbUrl = select("picture > img").attr("src")
val date = select("p.entry-date").text() val date = select("p.entry-date").text()
return TvSeriesEpisode( return newEpisode(url) {
title, name = title
null, episode = title.getIntFromText()
title.getIntFromText(), posterUrl = thumbUrl
url, addDate(date)
thumbUrl, }
date
)
} }
@ -142,7 +140,7 @@ class AkwamProvider : MainAPI() {
} }
} else { } else {
val episodes = doc.select("div.bg-primary2.p-4.col-lg-4.col-md-6.col-12").map { val episodes = doc.select("div.bg-primary2.p-4.col-lg-4.col-md-6.col-12").map {
it.toTvSeriesEpisode() it.toEpisode()
}.let { }.let {
val isReversed = it.lastOrNull()?.episode ?: 1 < it.firstOrNull()?.episode ?: 0 val isReversed = it.lastOrNull()?.episode ?: 1 < it.firstOrNull()?.episode ?: 0
if (isReversed) if (isReversed)

View file

@ -130,7 +130,7 @@ class AllMoviesForYouProvider : MainAPI() {
} }
if (list.isEmpty()) throw ErrorLoadingException("No Seasons Found") if (list.isEmpty()) throw ErrorLoadingException("No Seasons Found")
val episodeList = ArrayList<TvSeriesEpisode>() val episodeList = ArrayList<Episode>()
for (season in list) { for (season in list) {
val seasonResponse = app.get(season.second).text val seasonResponse = app.get(season.second).text
@ -144,15 +144,15 @@ class AllMoviesForYouProvider : MainAPI() {
val name = aName.text() val name = aName.text()
val href = aName.attr("href") val href = aName.attr("href")
val date = episode.selectFirst("> td.MvTbTtl > span")?.text() val date = episode.selectFirst("> td.MvTbTtl > span")?.text()
episodeList.add( episodeList.add(
TvSeriesEpisode( newEpisode(href) {
name, this.name = name
season.first, this.season = season.first
epNum, this.episode = epNum
fixUrl(href), this.posterUrl = fixUrlNull(poster)
fixUrlNull(poster), addDate(date)
date }
)
) )
} }
} }

View file

@ -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 if (videoUrl != null && videoUrl.contains("watch/null") || number == null) return null
return videoUrl?.let { return videoUrl?.let {
TvSeriesEpisode( Episode(
null, it,
null, null,
number, number,
it
) )
} }
} }
@ -101,7 +100,7 @@ class AsiaFlixProvider : MainAPI() {
"$mainUrl$dramaUrl/$_id".replace("drama-detail", "show-details"), "$mainUrl$dramaUrl/$_id".replace("drama-detail", "show-details"),
this@AsiaFlixProvider.name, this@AsiaFlixProvider.name,
TvType.AsianDrama, TvType.AsianDrama,
episodes.mapNotNull { it.toTvSeriesEpisode() }.sortedBy { it.episode }, episodes.mapNotNull { it.toEpisode() }.sortedBy { it.episode },
image, image,
releaseYear, releaseYear,
synopsis, synopsis,
@ -115,7 +114,8 @@ class AsiaFlixProvider : MainAPI() {
val headers = mapOf("X-Requested-By" to "asiaflix-web") val headers = mapOf("X-Requested-By" to "asiaflix-web")
val response = app.get("$apiUrl/dashboard", headers = headers).text 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 // Hack, because it can either be object or a list
val cleanedResponse = Regex(""""data":(\{.*?),\{"sectionName"""").replace(response) { val cleanedResponse = Regex(""""data":(\{.*?),\{"sectionName"""").replace(response) {
""""data":null},{"sectionName"""" """"data":null},{"sectionName""""
@ -145,14 +145,18 @@ class AsiaFlixProvider : MainAPI() {
): Boolean { ): Boolean {
if (isCasting) return false if (isCasting) return false
val headers = mapOf("X-Requested-By" to "asiaflix-web") 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())}" // val fixedUrl = "https://api.asiaflix.app/api/v2/utility/cors-proxy/playlist/${URLEncoder.encode(it, StandardCharsets.UTF_8.toString())}"
callback.invoke( callback.invoke(
ExtractorLink( ExtractorLink(
name, name,
name, name,
it, it,
"https://asianload1.com/", /** <------ This provider should be added instead */ "https://asianload1.com/",
/** <------ This provider should be added instead */
getQualityFromName(it), getQualityFromName(it),
URI(it).path.endsWith(".m3u8") URI(it).path.endsWith(".m3u8")
) )

View file

@ -242,11 +242,11 @@ open class BflixProvider() : MainAPI() {
val eptitle = it.selectFirst(".episode a span.name").text() val eptitle = it.selectFirst(".episode a span.name").text()
val secondtitle = it.selectFirst(".episode a span").text() val secondtitle = it.selectFirst(".episode a span").text()
.replace(Regex("(Episode (\\d+):|Episode (\\d+)-|Episode (\\d+))"),"") ?: "" .replace(Regex("(Episode (\\d+):|Episode (\\d+)-|Episode (\\d+))"),"") ?: ""
TvSeriesEpisode( Episode(
href,
secondtitle+eptitle, secondtitle+eptitle,
season, season,
episode, episode,
href,
) )
} }
val tvType = if (url.contains("/movie/") && episodes.size == 1) TvType.Movie else TvType.TvSeries val tvType = if (url.contains("/movie/") && episodes.size == 1) TvType.Movie else TvType.TvSeries

View file

@ -104,11 +104,11 @@ class CinecalidadProvider:MainAPI() {
val isValid = seasonid.size == 2 val isValid = seasonid.size == 2
val episode = if (isValid) seasonid.getOrNull(1) else null val episode = if (isValid) seasonid.getOrNull(1) else null
val season = if (isValid) seasonid.getOrNull(0) else null val season = if (isValid) seasonid.getOrNull(0) else null
TvSeriesEpisode( Episode(
href,
name, name,
season, season,
episode, episode,
href,
if (epThumb.contains("svg")) null else epThumb if (epThumb.contains("svg")) null else epThumb
) )
} }

View file

@ -1,12 +1,11 @@
package com.lagradost.cloudstream3.movieproviders package com.lagradost.cloudstream3.movieproviders
import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.annotation.JsonProperty
import com.fasterxml.jackson.module.kotlin.readValue
import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.utils.*
import com.lagradost.cloudstream3.utils.AppUtils.parseJson 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 mainUrl = "https://cuevana3.me"
@ -19,6 +18,7 @@ class CuevanaProvider:MainAPI() {
TvType.Movie, TvType.Movie,
TvType.TvSeries, TvType.TvSeries,
) )
override suspend fun getMainPage(): HomePageResponse { override suspend fun getMainPage(): HomePageResponse {
val items = ArrayList<HomePageList>() val items = ArrayList<HomePageList>()
val urls = listOf( val urls = listOf(
@ -28,7 +28,8 @@ class CuevanaProvider:MainAPI() {
items.add( items.add(
HomePageList( HomePageList(
"Series", "Series",
app.get("$mainUrl/serie", timeout = 120).document.select("section.home-series li").map { app.get("$mainUrl/serie", timeout = 120).document.select("section.home-series li")
.map {
val title = it.selectFirst("h2.Title").text() val title = it.selectFirst("h2.Title").text()
val poster = it.selectFirst("img.lazy").attr("data-src") val poster = it.selectFirst("img.lazy").attr("data-src")
val url = it.selectFirst("a").attr("href") val url = it.selectFirst("a").attr("href")
@ -69,6 +70,7 @@ class CuevanaProvider:MainAPI() {
if (items.size <= 0) throw ErrorLoadingException() if (items.size <= 0) throw ErrorLoadingException()
return HomePageResponse(items) return HomePageResponse(items)
} }
override suspend fun search(query: String): List<SearchResponse> { override suspend fun search(query: String): List<SearchResponse> {
val url = "$mainUrl/?s=${query}" val url = "$mainUrl/?s=${query}"
val document = app.get(url).document val document = app.get(url).document
@ -101,6 +103,7 @@ class CuevanaProvider:MainAPI() {
} }
} }
} }
override suspend fun load(url: String): LoadResponse? { override suspend fun load(url: String): LoadResponse? {
val soup = app.get(url, timeout = 120).document val soup = app.get(url, timeout = 120).document
val title = soup.selectFirst("h1.Title").text() val title = soup.selectFirst("h1.Title").text()
@ -108,29 +111,32 @@ class CuevanaProvider:MainAPI() {
val poster: String? = soup.selectFirst(".movtv-info div.Image img").attr("data-src") val poster: String? = soup.selectFirst(".movtv-info div.Image img").attr("data-src")
val year1 = soup.selectFirst("footer p.meta").toString() val year1 = soup.selectFirst("footer p.meta").toString()
val yearRegex = Regex("<span>(\\d+)</span>") 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 year = if (yearf.isNullOrBlank()) null else yearf.toIntOrNull()
val episodes = soup.select(".all-episodes li.TPostMv article").map { li -> val episodes = soup.select(".all-episodes li.TPostMv article").map { li ->
val href = li.select("a").attr("href") val href = li.select("a").attr("href")
val epThumb = 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 -> val seasonid = li.selectFirst("span.Year").text().let { str ->
str.split("x").mapNotNull { subStr -> subStr.toIntOrNull() } str.split("x").mapNotNull { subStr -> subStr.toIntOrNull() }
} }
val isValid = seasonid.size == 2 val isValid = seasonid.size == 2
val episode = if (isValid) seasonid.getOrNull(1) else null val episode = if (isValid) seasonid.getOrNull(1) else null
val season = if (isValid) seasonid.getOrNull(0) else null val season = if (isValid) seasonid.getOrNull(0) else null
TvSeriesEpisode( Episode(
href,
null, null,
season, season,
episode, episode,
href,
fixUrl(epThumb) fixUrl(epThumb)
) )
} }
val tags = soup.select("ul.InfoList li.AAIco-adjust:contains(Genero) a").map { it.text() } 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 tvType = if (episodes.isEmpty()) TvType.Movie else TvType.TvSeries
val recelement = if (tvType == TvType.TvSeries) "main section div.series_listado.series div.xxx" val recelement =
if (tvType == TvType.TvSeries) "main section div.series_listado.series div.xxx"
else "main section ul.MovieList li" else "main section ul.MovieList li"
val recommendations = val recommendations =
soup.select(recelement).mapNotNull { element -> soup.select(recelement).mapNotNull { element ->
@ -183,6 +189,7 @@ class CuevanaProvider:MainAPI() {
data class Femcuevana( data class Femcuevana(
@JsonProperty("url") val url: String, @JsonProperty("url") val url: String,
) )
override suspend fun loadLinks( override suspend fun loadLinks(
data: String, data: String,
isCasting: Boolean, isCasting: Boolean,
@ -192,12 +199,17 @@ class CuevanaProvider:MainAPI() {
app.get(data).document.select("div.TPlayer.embed_div iframe").apmap { app.get(data).document.select("div.TPlayer.embed_div iframe").apmap {
val iframe = fixUrl(it.attr("data-src")) val iframe = fixUrl(it.attr("data-src"))
if (iframe.contains("api.cuevana3.me/fembed/")) { 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 -> femregex.findAll(iframe).map { femreg ->
femreg.value femreg.value
}.toList().apmap { fem -> }.toList().apmap { fem ->
val key = fem.replace("https://api.cuevana3.me/fembed/?h=", "") 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", 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, "User-Agent" to USER_AGENT,
"Accept" to "application/json, text/javascript, */*; q=0.01", "Accept" to "application/json, text/javascript, */*; q=0.01",
"Accept-Language" to "en-US,en;q=0.5", "Accept-Language" to "en-US,en;q=0.5",
@ -208,8 +220,10 @@ class CuevanaProvider:MainAPI() {
"Connection" to "keep-alive", "Connection" to "keep-alive",
"Sec-Fetch-Dest" to "empty", "Sec-Fetch-Dest" to "empty",
"Sec-Fetch-Mode" to "cors", "Sec-Fetch-Mode" to "cors",
"Sec-Fetch-Site" to "same-origin",), "Sec-Fetch-Site" to "same-origin",
data = mapOf(Pair("h",key))).text ),
data = mapOf(Pair("h", key))
).text
val json = parseJson<Femcuevana>(url) val json = parseJson<Femcuevana>(url)
val link = json.url val link = json.url
if (link.contains("fembed")) { if (link.contains("fembed")) {
@ -218,13 +232,16 @@ class CuevanaProvider:MainAPI() {
} }
} }
if (iframe.contains("tomatomatela")) { 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 -> tomatoRegex.findAll(iframe).map { tomreg ->
tomreg.value tomreg.value
}.toList().apmap { tom -> }.toList().apmap { tom ->
val tomkey = tom.replace("//apialfa.tomatomatela.com/ir/player.php?h=", "") val tomkey = tom.replace("//apialfa.tomatomatela.com/ir/player.php?h=", "")
app.post("https://apialfa.tomatomatela.com/ir/rd.php", allowRedirects = false, app.post(
headers = mapOf("Host" to "apialfa.tomatomatela.com", "https://apialfa.tomatomatela.com/ir/rd.php", allowRedirects = false,
headers = mapOf(
"Host" to "apialfa.tomatomatela.com",
"User-Agent" to USER_AGENT, "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" 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", "Accept-Language" to "en-US,en;q=0.5",
@ -235,16 +252,21 @@ class CuevanaProvider:MainAPI() {
"Upgrade-Insecure-Requests" to "1", "Upgrade-Insecure-Requests" to "1",
"Sec-Fetch-Dest" to "iframe", "Sec-Fetch-Dest" to "iframe",
"Sec-Fetch-Mode" to "navigate", "Sec-Fetch-Mode" to "navigate",
"Sec-Fetch-Site" to "same-origin",), "Sec-Fetch-Site" to "same-origin",
),
data = mapOf(Pair("url", tomkey)) data = mapOf(Pair("url", tomkey))
).response.headers.values("location").apmap { loc -> ).response.headers.values("location").apmap { loc ->
if (loc.contains("goto_ddh.php")) { 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 -> 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 -> }.toList().apmap { gotolink ->
app.post("https://api.cuevana3.me/ir/redirect_ddh.php", allowRedirects = false, app.post(
headers = mapOf("Host" to "api.cuevana3.me", "https://api.cuevana3.me/ir/redirect_ddh.php",
allowRedirects = false,
headers = mapOf(
"Host" to "api.cuevana3.me",
"User-Agent" to USER_AGENT, "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" 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", "Accept-Language" to "en-US,en;q=0.5",
@ -255,7 +277,8 @@ class CuevanaProvider:MainAPI() {
"Upgrade-Insecure-Requests" to "1", "Upgrade-Insecure-Requests" to "1",
"Sec-Fetch-Dest" to "iframe", "Sec-Fetch-Dest" to "iframe",
"Sec-Fetch-Mode" to "navigate", "Sec-Fetch-Mode" to "navigate",
"Sec-Fetch-Site" to "same-origin",), "Sec-Fetch-Site" to "same-origin",
),
data = mapOf(Pair("url", gotolink)) data = mapOf(Pair("url", gotolink))
).response.headers.values("location").apmap { golink -> ).response.headers.values("location").apmap { golink ->
loadExtractor(golink, data, callback) loadExtractor(golink, data, callback)
@ -263,12 +286,15 @@ class CuevanaProvider:MainAPI() {
} }
} }
if (loc.contains("index.php?h=")) { 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 -> 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 -> }.toList().apmap { inlink ->
app.post("https://api.cuevana3.me/sc/r.php", allowRedirects = false, app.post(
headers = mapOf("Host" to "api.cuevana3.me", "https://api.cuevana3.me/sc/r.php", allowRedirects = false,
headers = mapOf(
"Host" to "api.cuevana3.me",
"User-Agent" to USER_AGENT, "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" 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", "Accept-Language" to "en-US,en;q=0.5",
@ -281,7 +307,8 @@ class CuevanaProvider:MainAPI() {
"Sec-Fetch-Dest" to "iframe", "Sec-Fetch-Dest" to "iframe",
"Sec-Fetch-Mode" to "navigate", "Sec-Fetch-Mode" to "navigate",
"Sec-Fetch-Site" to "same-origin", "Sec-Fetch-Site" to "same-origin",
"Sec-Fetch-User" to "?1",), "Sec-Fetch-User" to "?1",
),
data = mapOf(Pair("h", inlink)) data = mapOf(Pair("h", inlink))
).response.headers.values("location").apmap { link -> ).response.headers.values("location").apmap { link ->
loadExtractor(link, data, callback) loadExtractor(link, data, callback)

View file

@ -133,7 +133,7 @@ class DoramasYTProvider : MainAPI() {
val name = it.selectFirst(".dtlsflim p").text() val name = it.selectFirst(".dtlsflim p").text()
val link = it.selectFirst("a").attr("href") val link = it.selectFirst("a").attr("href")
val epThumb = it.selectFirst(".flimimg img.img1").attr("src") 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)) { return newAnimeLoadResponse(title, url, getType(type)) {
posterUrl = poster posterUrl = poster

View file

@ -118,7 +118,7 @@ class DramaSeeProvider : MainAPI() {
} }
// Episodes Links // Episodes Links
val episodeList = ArrayList<TvSeriesEpisode>() val episodeList = ArrayList<Episode>()
body?.select("ul.episodes > li")?.forEach { ep -> body?.select("ul.episodes > li")?.forEach { ep ->
val innerA = ep.select("a") ?: return@forEach val innerA = ep.select("a") ?: return@forEach
val count = innerA.select("span.episode")?.text()?.toIntOrNull() ?: 0 val count = innerA.select("span.episode")?.text()?.toIntOrNull() ?: 0
@ -141,7 +141,7 @@ class DramaSeeProvider : MainAPI() {
//Log.i(this.name, "Result => (listOfLinks) ${listOfLinks.toJson()}") //Log.i(this.name, "Result => (listOfLinks) ${listOfLinks.toJson()}")
episodeList.add( episodeList.add(
TvSeriesEpisode( Episode(
name = null, name = null,
season = null, season = null,
episode = count, episode = count,

View file

@ -1,9 +1,9 @@
package com.lagradost.cloudstream3.movieproviders 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.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 import org.jsoup.nodes.Element
class EgyBestProvider : MainAPI() { class EgyBestProvider : MainAPI() {
@ -109,7 +109,7 @@ class EgyBestProvider : MainAPI() {
this.actors = actors this.actors = actors
} }
} else { } else {
val episodes = ArrayList<TvSeriesEpisode>() val episodes = ArrayList<Episode>()
doc.select("#mainLoad > div:nth-child(2) > div.h_scroll > div a").map { doc.select("#mainLoad > div:nth-child(2) > div.h_scroll > div a").map {
it.attr("href") it.attr("href")
}.apmap { }.apmap {
@ -118,13 +118,11 @@ class EgyBestProvider : MainAPI() {
d.select("#mainLoad > div:nth-child(3) > div.movies_small a").map { eit -> 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() val ep = Regex("ep-(.....)").find(eit.attr("href"))?.groupValues?.getOrNull(1)?.getIntFromText()
episodes.add( episodes.add(
TvSeriesEpisode( Episode(
eit.attr("href"),
eit.select("span.title").text(), eit.select("span.title").text(),
season, season,
ep, ep,
eit.attr("href"),
null,
null
) )
) )
} }

View file

@ -2,8 +2,8 @@ package com.lagradost.cloudstream3.movieproviders
import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.utils.* import com.lagradost.cloudstream3.utils.ExtractorLink
import java.util.* import com.lagradost.cloudstream3.utils.loadExtractor
class EntrepeliculasyseriesProvider:MainAPI() { class EntrepeliculasyseriesProvider:MainAPI() {
override var mainUrl = "https://entrepeliculasyseries.nu" override var mainUrl = "https://entrepeliculasyseries.nu"
@ -102,11 +102,11 @@ class EntrepeliculasyseriesProvider:MainAPI() {
val isValid = seasonid.size == 2 val isValid = seasonid.size == 2
val episode = if (isValid) seasonid.getOrNull(1) else null val episode = if (isValid) seasonid.getOrNull(1) else null
val season = if (isValid) seasonid.getOrNull(0) else null val season = if (isValid) seasonid.getOrNull(0) else null
TvSeriesEpisode( Episode(
href,
null, null,
season, season,
episode, episode,
href,
fixUrl(epThumb) fixUrl(epThumb)
) )
} }

View file

@ -100,11 +100,11 @@ class FilmanProvider : MainAPI() {
val e = episode.text() val e = episode.text()
val regex = Regex("""\[s(\d{1,3})e(\d{1,3})]""").find(e) ?: return@mapNotNull null val regex = Regex("""\[s(\d{1,3})e(\d{1,3})]""").find(e) ?: return@mapNotNull null
val eid = regex.groups val eid = regex.groups
TvSeriesEpisode( Episode(
episode.attr("href"),
e.split("]")[1].trim(), e.split("]")[1].trim(),
eid[1]?.value?.toInt(), eid[1]?.value?.toInt(),
eid[2]?.value?.toInt(), eid[2]?.value?.toInt(),
episode.attr("href"),
) )
}.toMutableList() }.toMutableList()

View file

@ -139,7 +139,7 @@ class IHaveNoTvProvider : MainAPI() {
val episodes = if (isSeries) { val episodes = if (isSeries) {
container?.select(".episode")?.map { ep -> container?.select(".episode")?.map { ep ->
val thumb = ep.selectFirst("img").attr("src") 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 epLink = fixUrl(ep.selectFirst("a[title]").attr("href"))
val (season, epNum) = if (ep.selectFirst(".episodeMeta > strong") != null && val (season, epNum) = if (ep.selectFirst(".episodeMeta > strong") != null &&
ep.selectFirst(".episodeMeta > strong").html().contains("S") ep.selectFirst(".episodeMeta > strong").html().contains("S")
@ -150,7 +150,7 @@ class IHaveNoTvProvider : MainAPI() {
split?.get(1)?.toIntOrNull() split?.get(1)?.toIntOrNull()
) )
} else Pair<Int?, Int?>(null, null) } else Pair<Int?, Int?>(null, null)
val epDescription = ep.selectFirst(".episodeSynopsis")?.text()
year = Regex("""•?\s+(\d{4})\s+•""").find( year = Regex("""•?\s+(\d{4})\s+•""").find(
ep.selectFirst(".episodeMeta").text() ep.selectFirst(".episodeMeta").text()
)?.destructured?.component1()?.toIntOrNull() )?.destructured?.component1()?.toIntOrNull()
@ -158,16 +158,13 @@ class IHaveNoTvProvider : MainAPI() {
categories.addAll( categories.addAll(
ep.select(".episodeMeta > a[href*=\"/category/\"]").map { it.text().trim() }) ep.select(".episodeMeta > a[href*=\"/category/\"]").map { it.text().trim() })
TvSeriesEpisode( newEpisode(epLink) {
epTitle, this.name = ep.selectFirst("a[title]").attr("title")
season, this.season = season
epNum, this.episode = epNum
epLink, this.posterUrl = thumb
thumb, this.description = ep.selectFirst(".episodeSynopsis")?.text()
null, }
null,
epDescription
)
} }
} else { } else {
listOf(MovieLoadResponse( listOf(MovieLoadResponse(
@ -188,7 +185,7 @@ class IHaveNoTvProvider : MainAPI() {
} }
val poster = episodes?.firstOrNull().let { val poster = episodes?.firstOrNull().let {
if (isSeries && it != null) (it as TvSeriesEpisode).posterUrl if (isSeries && it != null) (it as Episode).posterUrl
else null else null
} }
@ -197,7 +194,7 @@ class IHaveNoTvProvider : MainAPI() {
url, url,
this.name, this.name,
TvType.TvSeries, TvType.TvSeries,
episodes!!.map { it as TvSeriesEpisode }, episodes!!.map { it as Episode },
poster, poster,
year, year,
description, description,

View file

@ -173,7 +173,7 @@ class KdramaHoodProvider : MainAPI() {
} }
} }
} }
TvSeriesEpisode( Episode(
name = null, name = null,
season = null, season = null,
episode = count, episode = count,

View file

@ -273,11 +273,11 @@ class LookMovieProvider : MainAPI() {
).toJson() ).toJson()
TvSeriesEpisode( Episode(
localData,
it.title, it.title,
it.season.toIntOrNull(), it.season.toIntOrNull(),
it.episode.toIntOrNull(), it.episode.toIntOrNull(),
localData
) )
}.toList() }.toList()

View file

@ -5,7 +5,6 @@ import com.fasterxml.jackson.module.kotlin.readValue
import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.LoadResponse.Companion.addImdbUrl import com.lagradost.cloudstream3.LoadResponse.Companion.addImdbUrl
import com.lagradost.cloudstream3.utils.AppUtils.parseJson 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.ExtractorLink
import com.lagradost.cloudstream3.utils.getQualityFromName import com.lagradost.cloudstream3.utils.getQualityFromName
import org.jsoup.Jsoup import org.jsoup.Jsoup
@ -87,7 +86,7 @@ class MeloMovieProvider : MainAPI() {
return url return url
} }
private fun serializeData(element: Element): String { private fun serializeData(element: Element): List<MeloMovieProvider.MeloMovieLink> {
val eps = element.select("> tbody > tr") val eps = element.select("> tbody > tr")
val parsed = eps.map { val parsed = eps.map {
try { try {
@ -99,7 +98,7 @@ class MeloMovieProvider : MainAPI() {
MeloMovieLink("", "") MeloMovieLink("", "")
} }
}.filter { it.link != "" && it.name != "" } }.filter { it.link != "" && it.name != "" }
return parsed.toJson() return parsed
} }
override suspend fun loadLinks( override suspend fun loadLinks(
@ -157,7 +156,7 @@ class MeloMovieProvider : MainAPI() {
addImdbUrl(imdbUrl) addImdbUrl(imdbUrl)
} }
} else if (type == 2) { } else if (type == 2) {
val episodes = ArrayList<TvSeriesEpisode>() val episodes = ArrayList<Episode>()
val seasons = document.select("div.accordion__card") val seasons = document.select("div.accordion__card")
?: throw ErrorLoadingException("No episodes found") ?: throw ErrorLoadingException("No episodes found")
for (s in seasons) { for (s in seasons) {
@ -172,7 +171,10 @@ class MeloMovieProvider : MainAPI() {
val links = val links =
e.selectFirst("> div.collapse > div > table.accordion__list") ?: continue e.selectFirst("> div.collapse > div > table.accordion__list") ?: continue
val data = serializeData(links) val data = serializeData(links)
episodes.add(TvSeriesEpisode(null, season, episode, data)) episodes.add(newEpisode(data) {
this.season = season
this.episode = episode
})
} }
} }
episodes.reverse() episodes.reverse()

View file

@ -63,9 +63,11 @@ class MyCimaProvider : MainAPI() {
override suspend fun search(query: String): List<SearchResponse> { override suspend fun search(query: String): List<SearchResponse> {
val q = query.replace(" ", "%20") val q = query.replace(" ", "%20")
val result = arrayListOf<SearchResponse>() val result = arrayListOf<SearchResponse>()
listOf("$mainUrl/search/$q", listOf(
"$mainUrl/search/$q",
"$mainUrl/search/$q/list/series/", "$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 val d = app.get(url).document
d.select("div.Grid--MycimaPosts div.GridItem").mapNotNull { d.select("div.Grid--MycimaPosts div.GridItem").mapNotNull {
if (it.text().contains("اعلان")) return@mapNotNull null if (it.text().contains("اعلان")) return@mapNotNull null
@ -86,7 +88,8 @@ class MyCimaProvider : MainAPI() {
doc.select("mycima.separated--top")?.attr("data-lazy-style")?.getImageURL() doc.select("mycima.separated--top")?.attr("data-lazy-style")?.getImageURL()
?.ifEmpty { doc.select("meta[itemprop=\"thumbnailUrl\"]")?.attr("content") } ?.ifEmpty { doc.select("meta[itemprop=\"thumbnailUrl\"]")?.attr("content") }
?.ifEmpty { doc.select("mycima.separated--top")?.attr("style")?.getImageURL() } ?.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() val title = doc.select("div.Title--Content--Single-begin h1").text()
.replace("($year)", "") .replace("($year)", "")
.replace("مشاهدة|فيلم|مسلسل|مترجم|انمي".toRegex(), "") .replace("مشاهدة|فيلم|مسلسل|مترجم|انمي".toRegex(), "")
@ -96,7 +99,8 @@ class MyCimaProvider : MainAPI() {
it.text().contains("المدة") it.text().contains("المدة")
}?.text()?.getIntFromText() }?.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() } val tags = doc.select("li:nth-child(3) > p > a").map { it.text() }
@ -107,7 +111,8 @@ class MyCimaProvider : MainAPI() {
?: return@mapNotNull null ?: return@mapNotNull null
Actor(name, image) Actor(name, image)
} }
val recommendations = doc.select("div.Grid--MycimaPosts div.GridItem")?.mapNotNull { element -> val recommendations =
doc.select("div.Grid--MycimaPosts div.GridItem")?.mapNotNull { element ->
element.toSearchResponse() element.toSearchResponse()
} }
@ -127,26 +132,78 @@ class MyCimaProvider : MainAPI() {
addActors(actors) addActors(actors)
} }
} else { } else {
val episodes = ArrayList<TvSeriesEpisode>() val episodes = ArrayList<Episode>()
val seasons = doc.select("div.List--Seasons--Episodes a").not(".selected").map { val seasons = doc.select("div.List--Seasons--Episodes a").not(".selected").map {
it.attr("href") it.attr("href")
} }
val moreButton = doc.select("div.MoreEpisodes--Button") 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") 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))} .apmap {
episodes.add(
Episode(
it.attr("href"),
it.text(),
season,
it.text().getIntFromText(),
)
)
}
if (moreButton.isNotEmpty()) { if (moreButton.isNotEmpty()) {
val n = doc.select("div.Seasons--Episodes div.Episodes--Seasons--Episodes a").size 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 totals =
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) 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 -> mEPS.apmap { it ->
if (it != null) { if (it != null) {
if (it > totals!!) return@apmap if (it > totals!!) return@apmap
val ajaxURL = "$mainUrl/AjaxCenter/MoreEpisodes/${moreButton.attr("data-term")}/$it" val ajaxURL =
"$mainUrl/AjaxCenter/MoreEpisodes/${moreButton.attr("data-term")}/$it"
val jsonResponse = app.get(ajaxURL) val jsonResponse = app.get(ajaxURL)
val json = parseJson<MoreEPS>(jsonResponse.text) val json = parseJson<MoreEPS>(jsonResponse.text)
val document = Jsoup.parse(json.output?.replace("""\""", "")) 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)) } document.select("a").map {
episodes.add(
Episode(
it.attr("href"),
it.text(),
season,
it.text().getIntFromText(),
)
)
}
} }
} }
} }
@ -155,27 +212,85 @@ class MyCimaProvider : MainAPI() {
if (surl.contains("%d9%85%d8%af%d8%a8%d9%84%d8%ac")) return@apmap if (surl.contains("%d9%85%d8%af%d8%a8%d9%84%d8%ac")) return@apmap
val seasonsite = app.get(surl).document val seasonsite = app.get(surl).document
val fmoreButton = seasonsite.select("div.MoreEpisodes--Button") val fmoreButton = seasonsite.select("div.MoreEpisodes--Button")
val fseason = seasonsite.select("div.List--Seasons--Episodes a.selected").text().getIntFromText() ?: 1 val fseason = seasonsite.select("div.List--Seasons--Episodes a.selected").text()
.getIntFromText() ?: 1
seasonsite.select("div.Seasons--Episodes div.Episodes--Seasons--Episodes a") 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))} .map {
episodes.add(
Episode(
it.attr("href"),
it.text(),
fseason,
it.text().getIntFromText(),
)
)
}
if (fmoreButton.isNotEmpty()) { if (fmoreButton.isNotEmpty()) {
val n = seasonsite.select("div.Seasons--Episodes div.Episodes--Seasons--Episodes a").size val n =
val totals = seasonsite.select("div.Episodes--Seasons--Episodes a").first().text().getIntFromText() seasonsite.select("div.Seasons--Episodes div.Episodes--Seasons--Episodes a").size
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 =
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 -> mEPS.apmap { it ->
if (it != null) { if (it != null) {
if (it > totals!!) return@apmap if (it > totals!!) return@apmap
val ajaxURL = "$mainUrl/AjaxCenter/MoreEpisodes/${fmoreButton.attr("data-term")}/$it" val ajaxURL =
"$mainUrl/AjaxCenter/MoreEpisodes/${fmoreButton.attr("data-term")}/$it"
val jsonResponse = app.get(ajaxURL) val jsonResponse = app.get(ajaxURL)
val json = parseJson<MoreEPS>(jsonResponse.text) val json = parseJson<MoreEPS>(jsonResponse.text)
val document = Jsoup.parse(json.output?.replace("""\""", "")) 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)) } document.select("a").map {
episodes.add(
Episode(
it.attr("href"),
it.text(),
fseason,
it.text().getIntFromText(),
)
)
}
} }
} }
} else return@apmap } else return@apmap
} }
} }
newTvSeriesLoadResponse(title, url, TvType.TvSeries, episodes.distinct().sortedBy { it.episode }) { newTvSeriesLoadResponse(
title,
url,
TvType.TvSeries,
episodes.distinct().sortedBy { it.episode }) {
this.duration = duration this.duration = duration
this.posterUrl = posterUrl this.posterUrl = posterUrl
this.tags = tags this.tags = tags
@ -186,6 +301,7 @@ class MyCimaProvider : MainAPI() {
} }
} }
} }
override suspend fun loadLinks( override suspend fun loadLinks(
data: String, data: String,
isCasting: Boolean, isCasting: Boolean,
@ -198,7 +314,9 @@ class MyCimaProvider : MainAPI() {
callback.invoke( callback.invoke(
ExtractorLink( ExtractorLink(
this.name, this.name,
this.name + " - ${linkElement.select("resolution").text().getIntFromText()}p", this.name + " - ${
linkElement.select("resolution").text().getIntFromText()
}p",
linkElement.attr("href"), linkElement.attr("href"),
this.mainUrl, this.mainUrl,
2 2

View file

@ -2,9 +2,7 @@ package com.lagradost.cloudstream3.movieproviders
import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.extractorApis
import com.lagradost.cloudstream3.utils.loadExtractor import com.lagradost.cloudstream3.utils.loadExtractor
import kotlin.collections.ArrayList
class PeliSmartProvider: MainAPI() { class PeliSmartProvider: MainAPI() {
override var mainUrl = "https://pelismart.com" override var mainUrl = "https://pelismart.com"
@ -105,11 +103,11 @@ class PeliSmartProvider: MainAPI() {
val isValid = seasonid?.size == 2 val isValid = seasonid?.size == 2
val episode = if (isValid) seasonid?.getOrNull(1) else null val episode = if (isValid) seasonid?.getOrNull(1) else null
val season = if (isValid) seasonid?.getOrNull(0) else null val season = if (isValid) seasonid?.getOrNull(0) else null
TvSeriesEpisode( Episode(
href,
name, name,
season, season,
episode, episode,
href,
) )
} }
return when (val tvType = if (episodes.isEmpty()) TvType.Movie else TvType.TvSeries) { return when (val tvType = if (episodes.isEmpty()) TvType.Movie else TvType.TvSeries) {

View file

@ -127,7 +127,7 @@ class PelisflixProvider : MainAPI() {
} }
if (list.isEmpty()) throw ErrorLoadingException("No Seasons Found") if (list.isEmpty()) throw ErrorLoadingException("No Seasons Found")
val episodeList = ArrayList<TvSeriesEpisode>() val episodeList = ArrayList<Episode>()
for ((seasonInt, seasonUrl) in list) { for ((seasonInt, seasonUrl) in list) {
val seasonDocument = app.get(seasonUrl).document val seasonDocument = app.get(seasonUrl).document
@ -141,14 +141,13 @@ class PelisflixProvider : MainAPI() {
val href = aName.attr("href") val href = aName.attr("href")
val date = episode.selectFirst("> td.MvTbTtl > span")?.text() val date = episode.selectFirst("> td.MvTbTtl > span")?.text()
episodeList.add( episodeList.add(
TvSeriesEpisode( newEpisode(href) {
name, this.name = name
seasonInt, this.season = seasonInt
epNum, this.episode = epNum
href, this.posterUrl = fixUrlNull(epthumb)
fixUrlNull(epthumb), addDate(date)
date }
)
) )
} }
} }

View file

@ -111,11 +111,11 @@ class PelisplusHDProvider:MainAPI() {
val isValid = seasonid.size == 2 val isValid = seasonid.size == 2
val episode = if (isValid) seasonid.getOrNull(1) else null val episode = if (isValid) seasonid.getOrNull(1) else null
val season = if (isValid) seasonid.getOrNull(0) else null val season = if (isValid) seasonid.getOrNull(0) else null
TvSeriesEpisode( Episode(
href,
name, name,
season, season,
episode, episode,
href,
) )
} }

View file

@ -1,9 +1,13 @@
package com.lagradost.cloudstream3.movieproviders package com.lagradost.cloudstream3.movieproviders
import com.lagradost.cloudstream3.* 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 import org.jsoup.Jsoup
/** Needs to inherit from MainAPI() to /** Needs to inherit from MainAPI() to
* make the app know what functions to call * 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 description = soup.selectFirst(".post-entry")?.text()?.trim()
val poster = soup.selectFirst("head meta[property=og:image]").attr("content") 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 episodes = soup.select(".listing.items.lists > .video-block").map { li ->
val href = fixUrl(li.selectFirst("a").attr("href")) val href = fixUrl(li.selectFirst("a").attr("href"))
val regexseason = Regex("(-[Tt]emporada-(\\d+)-[Cc]apitulo-(\\d+))") 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 epThumb = fixUrl(li.selectFirst("img").attr("src"))
val epDate = li.selectFirst(".meta > .date").text() val epDate = li.selectFirst(".meta > .date").text()
if(year == null) {
year = epDate?.split("-")?.get(0)?.toIntOrNull()
}
newEpisode(li.selectFirst("a").attr("href")) {
TvSeriesEpisode( this.season = season
null, this.episode = episode
season, this.posterUrl = epThumb
episode, addDate(epDate)
fixUrl(li.selectFirst("a").attr("href")), }
epThumb,
epDate
)
}.reversed() }.reversed()
val year = episodes.first().date?.split("-")?.get(0)?.toIntOrNull()
// Make sure to get the type right to display the correct UI. // Make sure to get the type right to display the correct UI.
val tvType = if (episodes.size == 1 && episodes[0].name == title) TvType.Movie else TvType.TvSeries val tvType = if (episodes.size == 1 && episodes[0].name == title) TvType.Movie else TvType.TvSeries
@ -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() // 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. // The data are usually links, but can be any other string to help aid loading the links.
override suspend fun loadLinks( override suspend fun loadLinks(
data: String, data: String,

View file

@ -138,7 +138,7 @@ class PinoyHDXyzProvider : MainAPI() {
} }
// Try looking for episodes, for series // 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") val bodyText = body?.select("div.section-cotent1.col-md-12")?.select("section")
?.select("script")?.toString() ?: "" ?.select("script")?.toString() ?: ""
//Log.i(this.name, "Result => (bodyText) ${bodyText}") //Log.i(this.name, "Result => (bodyText) ${bodyText}")
@ -151,7 +151,7 @@ class PinoyHDXyzProvider : MainAPI() {
val listEpStream = listOf(ep.trim()).toJson() val listEpStream = listOf(ep.trim()).toJson()
//Log.i(this.name, "Result => (ep $count) $listEpStream") //Log.i(this.name, "Result => (ep $count) $listEpStream")
episodeList.add( episodeList.add(
TvSeriesEpisode( Episode(
name = null, name = null,
season = null, season = null,
episode = count, episode = count,

View file

@ -159,7 +159,7 @@ class PinoyMoviePediaProvider : MainAPI() {
// Parse episodes if series // Parse episodes if series
if (isTvSeries) { if (isTvSeries) {
val episodeList = ArrayList<TvSeriesEpisode>() val episodeList = ArrayList<Episode>()
val epLinks = playcontainer?.select("div > div > div.source-box") val epLinks = playcontainer?.select("div > div > div.source-box")
//Log.i(this.name, "Result => (epList) ${epList}") //Log.i(this.name, "Result => (epList) ${epList}")
body?.select("div#playeroptions > ul > li")?.forEach { ep -> body?.select("div#playeroptions > ul > li")?.forEach { ep ->
@ -175,7 +175,7 @@ class PinoyMoviePediaProvider : MainAPI() {
val streamEpLink = listOf(href.trim()).toJson() val streamEpLink = listOf(href.trim()).toJson()
//Log.i(this.name, "Result => (streamEpLink $epNum) $streamEpLink") //Log.i(this.name, "Result => (streamEpLink $epNum) $streamEpLink")
episodeList.add( episodeList.add(
TvSeriesEpisode( Episode(
name = null, name = null,
season = null, season = null,
episode = epNum, episode = epNum,

View file

@ -124,7 +124,7 @@ class SeriesflixProvider : MainAPI() {
} }
if (list.isEmpty()) throw ErrorLoadingException("No Seasons Found") if (list.isEmpty()) throw ErrorLoadingException("No Seasons Found")
val episodeList = ArrayList<TvSeriesEpisode>() val episodeList = ArrayList<Episode>()
for (season in list) { for (season in list) {
val seasonDocument = app.get(season.second).document val seasonDocument = app.get(season.second).document
@ -138,14 +138,13 @@ class SeriesflixProvider : MainAPI() {
val href = aName.attr("href") val href = aName.attr("href")
val date = episode.selectFirst("> td.MvTbTtl > span")?.text() val date = episode.selectFirst("> td.MvTbTtl > span")?.text()
episodeList.add( episodeList.add(
TvSeriesEpisode( newEpisode(href) {
name, this.name = name
season.first, this.season = season.first
epNum, this.episode = epNum
href, this.posterUrl = fixUrlNull(epthumb)
fixUrlNull(epthumb), addDate(date)
date }
)
) )
} }
} }

View file

@ -11,7 +11,6 @@ import com.lagradost.cloudstream3.animeproviders.ZoroProvider
import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall
import com.lagradost.cloudstream3.network.AppResponse import com.lagradost.cloudstream3.network.AppResponse
import com.lagradost.cloudstream3.utils.AppUtils.parseJson 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.AppUtils.tryParseJson
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.ExtractorLink
@ -214,7 +213,7 @@ open class SflixProvider : MainAPI() {
val comingSoon = sourceIds.isEmpty() val comingSoon = sourceIds.isEmpty()
return newMovieLoadResponse(title, url, TvType.Movie, sourceIds.toJson()) { return newMovieLoadResponse(title, url, TvType.Movie, sourceIds) {
this.year = year this.year = year
this.posterUrl = posterUrl this.posterUrl = posterUrl
this.plot = plot this.plot = plot
@ -228,7 +227,7 @@ open class SflixProvider : MainAPI() {
} }
} else { } else {
val seasonsDocument = app.get("$mainUrl/ajax/v2/tv/seasons/$id").document 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") var seasonItems = seasonsDocument.select("div.dropdown-menu.dropdown-menu-model > a")
if (seasonItems.isNullOrEmpty()) if (seasonItems.isNullOrEmpty())
seasonItems = seasonsDocument.select("div.dropdown-menu > a.dropdown-item") seasonItems = seasonsDocument.select("div.dropdown-menu > a.dropdown-item")
@ -260,13 +259,12 @@ open class SflixProvider : MainAPI() {
} ?: episode } ?: episode
episodes.add( episodes.add(
TvSeriesEpisode( newEpisode(Pair(url, episodeData)) {
episodeTitle?.removePrefix("Episode $episodeNum: "), this.posterUrl = fixUrlNull(episodePosterUrl)
season + 1, this.name = episodeTitle?.removePrefix("Episode $episodeNum: ")
episodeNum, this.season = season + 1
Pair(url, episodeData).toJson(), this.episode = episodeNum
fixUrlNull(episodePosterUrl) }
)
) )
} }
} }
@ -370,7 +368,8 @@ open class SflixProvider : MainAPI() {
val posterUrl = img.attr("data-src") ?: img.attr("src") val posterUrl = img.attr("data-src") ?: img.attr("src")
val href = fixUrl(inner.select("a").attr("href")) val href = fixUrl(inner.select("a").attr("href"))
val isMovie = href.contains("/movie/") 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 rating: Int? = null
var year: Int? = null var year: Int? = null
var quality: SearchQuality? = null var quality: SearchQuality? = null

View file

@ -75,7 +75,7 @@ class SoaptwoDayProvider:MainAPI() {
val episodes = soup.select("div.alert > div > div > a").mapNotNull { val episodes = soup.select("div.alert > div > div > a").mapNotNull {
val link = fixUrlNull(it?.attr("href")) ?: return@mapNotNull null val link = fixUrlNull(it?.attr("href")) ?: return@mapNotNull null
val name = it?.text()?.replace(Regex("(^(\\d+)\\.)"),"") val name = it?.text()?.replace(Regex("(^(\\d+)\\.)"),"")
TvSeriesEpisode( Episode(
name = name, name = name,
data = link data = link
) )

View file

@ -420,13 +420,15 @@ class TheFlixToProvider : MainAPI() {
) )
//{"affiliateCode":"","pathname":"/movie/696806-the-adam-project"} //{"affiliateCode":"","pathname":"/movie/696806-the-adam-project"}
val data = mapOf("affiliateCode" to "", "pathname" to url.removePrefix(mainUrl)) val data = mapOf("affiliateCode" to "", "pathname" to url.removePrefix(mainUrl))
val resp = app.post(optionsUrl, headers = mapOf( val resp = app.post(
optionsUrl, headers = mapOf(
"User-Agent" to USER_AGENT, "User-Agent" to USER_AGENT,
"Content-Type" to "application/json;charset=UTF-8", "Content-Type" to "application/json;charset=UTF-8",
"Accept" to "application/json, text/plain, */*", "Accept" to "application/json, text/plain, */*",
"Origin" to url, "Origin" to url,
"Referer" to mainUrl, "Referer" to mainUrl,
), data = data) ), data = data
)
latestCookies = resp.cookies latestCookies = resp.cookies
val newData = getLoadMan(url) val newData = getLoadMan(url)
@ -442,7 +444,7 @@ class TheFlixToProvider : MainAPI() {
override suspend fun load(url: String): LoadResponse? { override suspend fun load(url: String): LoadResponse? {
val tvtype = if (url.contains("movie")) TvType.Movie else TvType.TvSeries val tvtype = if (url.contains("movie")) TvType.Movie else TvType.TvSeries
val json = getLoadMainRetry(url) val json = getLoadMainRetry(url)
val episodes = ArrayList<TvSeriesEpisode>() val episodes = ArrayList<Episode>()
val isMovie = tvtype == TvType.Movie val isMovie = tvtype == TvType.Movie
val pageMain = json.props.pageProps val pageMain = json.props.pageProps
@ -471,11 +473,11 @@ class TheFlixToProvider : MainAPI() {
val test = epi.videos val test = epi.videos
val ratinginfo = (epi.voteAverage)?.times(10)?.toInt() val ratinginfo = (epi.voteAverage)?.times(10)?.toInt()
val rating = if (ratinginfo?.equals(0) == true) null else ratinginfo 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, title,
seasonum, seasonum,
episodenu, episodenu,
"$mainUrl/tv-show/$movieId-${cleanTitle(movietitle)}/season-$seasonum/episode-$episodenu",
description = epDesc!!, description = epDesc!!,
posterUrl = seasonPoster, posterUrl = seasonPoster,
rating = rating, rating = rating,

View file

@ -28,7 +28,8 @@ class VfSerieProvider : MainAPI() {
for (item in items) { for (item in items) {
val href = item.attr("href") 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/) 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 continue
@ -37,7 +38,17 @@ class VfSerieProvider : MainAPI() {
val year = item.selectFirst("> span.Year").text()?.toIntOrNull() 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 return returnValue
} }
@ -99,7 +110,8 @@ class VfSerieProvider : MainAPI() {
val response = app.get(url).text val response = app.get(url).text
val document = Jsoup.parse(response) val document = Jsoup.parse(response)
val title = 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") ?: throw ErrorLoadingException("Service might be unavailable")
@ -109,7 +121,8 @@ class VfSerieProvider : MainAPI() {
//val duration = document.select("span.Time").text()?.toIntOrNull() //val duration = document.select("span.Time").text()?.toIntOrNull()
val backgroundPoster = 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() val descript = document.selectFirst("div.Description > p").text()
@ -124,7 +137,7 @@ class VfSerieProvider : MainAPI() {
} }
if (list.isEmpty()) throw ErrorLoadingException("No Seasons Found") if (list.isEmpty()) throw ErrorLoadingException("No Seasons Found")
val episodeList = ArrayList<TvSeriesEpisode>() val episodeList = ArrayList<Episode>()
for (season in list) { for (season in list) {
val episodes = document.select("table > tbody > tr") val episodes = document.select("table > tbody > tr")
@ -132,20 +145,20 @@ class VfSerieProvider : MainAPI() {
episodes.forEach { episode -> episodes.forEach { episode ->
val epNum = episode.selectFirst("> span.Num")?.text()?.toIntOrNull() val epNum = episode.selectFirst("> span.Num")?.text()?.toIntOrNull()
val poster = 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 aName = episode.selectFirst("> td.MvTbTtl > a")
val date = episode.selectFirst("> td.MvTbTtl > span")?.text()?.toString() val date = episode.selectFirst("> td.MvTbTtl > span")?.text()?.toString()
val name = aName.text() val name = aName.text()
val href = aName.attr("href") val href = aName.attr("href")
episodeList.add( episodeList.add(
TvSeriesEpisode( newEpisode(href) {
name, this.name = name
season, this.season = season
epNum, this.episode = epNum
href, this.posterUrl = poster
poster, addDate(date)
date }
)
) )
} }
} }

View file

@ -85,6 +85,7 @@ open class VidstreamProviderTemplate : MainAPI() {
val description = soup.selectFirst(".post-entry")?.text()?.trim() val description = soup.selectFirst(".post-entry")?.text()?.trim()
var poster: String? = null var poster: String? = null
var year : Int? = null
val episodes = val episodes =
soup.select(".listing.items.lists > .video-block").withIndex().map { (_, li) -> 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() val epNum = Regex("""Episode (\d+)""").find(epTitle)?.destructured?.component1()
?.toIntOrNull() ?.toIntOrNull()
if(year == null) {
TvSeriesEpisode( year = epDate?.split("-")?.get(0)?.toIntOrNull()
epTitle, }
null, newEpisode(li.selectFirst("a").attr("href")) {
epNum, this.episode = epNum
fixUrl(li.selectFirst("a").attr("href")), this.posterUrl = epThumb
epThumb, addDate(epDate)
epDate }
)
}.reversed() }.reversed()
val year = episodes.first().date?.split("-")?.get(0)?.toIntOrNull()
// Make sure to get the type right to display the correct UI. // Make sure to get the type right to display the correct UI.
val tvType = val tvType =
if (episodes.size == 1 && episodes[0].name == title) TvType.Movie else TvType.TvSeries 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() // 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. // The data are usually links, but can be any other string to help aid loading the links.
override suspend fun loadLinks( override suspend fun loadLinks(
data: String, data: String,

View file

@ -156,7 +156,7 @@ class WatchAsianProvider : MainAPI() {
val regex = "(?<=episode-).*?(?=.html)".toRegex() val regex = "(?<=episode-).*?(?=.html)".toRegex()
val count = regex.find(epLink, mainUrl.length)?.value?.toIntOrNull() ?: 0 val count = regex.find(epLink, mainUrl.length)?.value?.toIntOrNull() ?: 0
//Log.i(this.name, "Result => $epLink (regexYear) ${count}") //Log.i(this.name, "Result => $epLink (regexYear) ${count}")
TvSeriesEpisode( Episode(
name = null, name = null,
season = null, season = null,
episode = count, episode = count,

View file

@ -112,11 +112,11 @@ class FrenchStreamProvider : MainAPI() {
if (poster == null) { if (poster == null) {
poster = a.selectFirst("div.fposter > img")?.attr("src") poster = a.selectFirst("div.fposter > img")?.attr("src")
} }
TvSeriesEpisode( Episode(
fixUrl(url).plus("-episodenumber:$epNum"),
epTitle, epTitle,
null, null,
epNum, epNum,
fixUrl(url).plus("-episodenumber:$epNum"),
null, // episode Thumbnail null, // episode Thumbnail
null // episode date null // episode date
) )

View file

@ -364,8 +364,8 @@ class ResultViewModel : ViewModel() {
filterName(i.name), filterName(i.name),
i.posterUrl, i.posterUrl,
episode, episode,
null, // TODO FIX SEASON i.season,
i.url, i.data,
apiName, apiName,
mainId + index + 1 + idIndex * 100000, mainId + index + 1 + idIndex * 100000,
index, index,