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
versionCode 45
versionName "2.9.17"
versionName "2.9.18"
resValue "string", "app_version",
"${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.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(

View File

@ -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)

View File

@ -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)) {

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 {
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!!"
)
)

View File

@ -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
)

View File

@ -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,

View File

@ -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")

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 =
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"))

View File

@ -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()
)

View File

@ -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()
)

View File

@ -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

View File

@ -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

View File

@ -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 ->

View File

@ -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,

View File

@ -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 =

View File

@ -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")

View File

@ -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,

View File

@ -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)

View File

@ -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)

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
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")
)

View File

@ -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

View File

@ -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
)
}

View File

@ -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)
}

View File

@ -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

View File

@ -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,

View File

@ -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
)
)
}

View File

@ -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)
)
}

View File

@ -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()

View File

@ -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,

View File

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

View File

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

View File

@ -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()

View File

@ -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
}
}

View File

@ -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) {

View File

@ -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)
}
)
}
}

View File

@ -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,
)
}

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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)
}
)
}
}

View File

@ -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

View File

@ -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
)

View File

@ -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,

View File

@ -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)
}
)
}
}

View File

@ -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,

View File

@ -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,

View File

@ -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
)

View File

@ -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,