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