cloudstream-extensions-hexated/Xcineio/src/main/kotlin/com/hexated/XCine.kt

211 lines
8.4 KiB
Kotlin

package com.hexated
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.*
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.ExtractorLink
import com.lagradost.cloudstream3.utils.Qualities
import com.lagradost.cloudstream3.utils.loadExtractor
class XCine : MainAPI() {
override var name = "XCine"
override var mainUrl = "https://xcine.info"
override var lang = "de"
override val hasQuickSearch = true
override val usesWebView = false
override val hasMainPage = true
override val supportedTypes = setOf(TvType.TvSeries, TvType.Movie)
private val mainAPI = "https://api.xcine.info"
override val mainPage = mainPageOf(
"$mainAPI/data/browse/?lang=2&keyword=&year=&rating=&votes=&genre=&country=&cast=&directors=&type=movies&order_by=trending&page=" to "Trending",
"$mainAPI/data/browse/?lang=2&keyword=&year=&rating=&votes=&genre=&country=&cast=&directors=&type=movies&order_by=Views&page=" to "Most View Filme",
"$mainAPI/data/browse/?lang=2&keyword=&year=&rating=&votes=&genre=&country=&cast=&directors=&type=tvseries&order_by=Trending&page=" to "Trending Serien",
"$mainAPI/data/browse/?lang=2&keyword=&year=&rating=&votes=&genre=&country=&cast=&directors=&type=movies&order_by=Updates&page=" to "Updated Filme",
"$mainAPI/data/browse/?lang=2&keyword=&year=&rating=&votes=&genre=&country=&cast=&directors=&type=tvseries&order_by=Updates&page=" to "Updated Serien",
)
private fun getImageUrl(link: String?): String? {
if (link == null) return null
return if (link.startsWith("/")) "https://image.tmdb.org/t/p/w500/$link" else link
}
override suspend fun getMainPage(
page: Int,
request: MainPageRequest
): HomePageResponse {
val home =
app.get(request.data + page, referer = "$mainUrl/")
.parsedSafe<MediaResponse>()?.movies?.mapNotNull { res ->
res.toSearchResponse()
} ?: throw ErrorLoadingException()
return newHomePageResponse(request.name, home)
}
private fun Media.toSearchResponse(): SearchResponse? {
return newAnimeSearchResponse(
title ?: original_title ?: return null,
// Data(_id).toJson(),
"$_id",
TvType.TvSeries,
false
) {
this.posterUrl = getImageUrl(poster_path ?: backdrop_path)
addDub(last_updated_epi?.toIntOrNull())
addSub(totalEpisodes?.toIntOrNull())
}
}
override suspend fun quickSearch(query: String): List<SearchResponse> = search(query)
override suspend fun search(query: String): List<SearchResponse> {
return app.get(
"$mainAPI/data/browse/?lang=2&keyword=$query&year=&rating=&votes=&genre=&country=&cast=&directors=&type=&order_by=&page=1",
referer = "$mainUrl/"
).parsedSafe<MediaResponse>()?.movies?.mapNotNull { res ->
res.toSearchResponse()
} ?: throw ErrorLoadingException()
}
override suspend fun load(url: String): LoadResponse? {
val id = url.replace("$mainUrl/", "")
val res = app.get("$mainAPI/data/watch/?_id=$id", referer = "$mainUrl/")
.parsedSafe<MediaDetail>() ?: throw ErrorLoadingException()
val type = if (res.tv == 1) "tv" else "movie"
val recommendations =
app.get("$mainAPI/data/related_movies/?lang=2&cat=$type&_id=$id&server=0").text.let {
tryParseJson<List<Media>>(it)
}?.mapNotNull {
it.toSearchResponse()
}
return if (type == "tv") {
val episodes = mutableListOf<Episode>()
val json =
app.get("$mainAPI/data/seasons/?lang=2&original_title=${res.original_title}").text.let {
tryParseJson<List<Season>>(it)
}
json?.map { season ->
season.streams?.distinctBy { it.e }?.map { eps ->
episodes.add(Episode(data = season.streams.filter { it.e == eps.e }
.map { Link(it.stream) }
.toJson(), episode = eps.e, season = season.s))
}
}
newTvSeriesLoadResponse(
res.original_title ?: res.title ?: return null,
url,
TvType.TvSeries,
episodes
) {
this.posterUrl = getImageUrl(res.backdrop_path ?: res.poster_path)
this.year = res.year
this.plot = res.storyline ?: res.overview
this.tags = listOf(res.genres ?: "")
this.recommendations = recommendations
}
} else {
newMovieLoadResponse(
res.original_title ?: res.title ?: return null,
url,
TvType.Movie,
res.streams?.map { Link(it.stream) }?.toJson()
) {
this.posterUrl = getImageUrl(res.backdrop_path ?: res.poster_path)
this.year = res.year
this.plot = res.storyline ?: res.overview
this.tags = listOf(res.genres ?: "")
this.recommendations = recommendations
}
}
}
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
val loadData = parseJson<List<Link>>(data)
loadData.apmap {
val link = fixUrlNull(it.link) ?: return@apmap null
if(link.startsWith("https://dl.streamcloud")) {
callback.invoke(
ExtractorLink(
this.name,
this.name,
link,
"",
Qualities.Unknown.value
)
)
} else {
loadExtractor(
link,
"$mainUrl/",
subtitleCallback,
callback
)
}
}
return true
}
data class Link(
val link: String?,
)
data class Season(
@JsonProperty("_id") val _id: String? = null,
@JsonProperty("s") val s: Int? = null,
@JsonProperty("title") val title: String? = null,
@JsonProperty("year") val year: Int? = null,
@JsonProperty("streams") val streams: ArrayList<Streams>? = arrayListOf(),
)
data class Streams(
@JsonProperty("_id") val _id: String? = null,
@JsonProperty("stream") val stream: String? = null,
@JsonProperty("e") val e: Int? = null,
@JsonProperty("e_title") val e_title: String? = null,
)
data class MediaDetail(
@JsonProperty("_id") val _id: String? = null,
@JsonProperty("tv") val tv: Int? = null,
@JsonProperty("original_title") val original_title: String? = null,
@JsonProperty("title") val title: String? = null,
@JsonProperty("poster_path") val poster_path: String? = null,
@JsonProperty("backdrop_path") val backdrop_path: String? = null,
@JsonProperty("imdb_id") val imdb_id: String? = null,
@JsonProperty("year") val year: Int? = null,
@JsonProperty("rating") val rating: String? = null,
@JsonProperty("genres") val genres: String? = null,
@JsonProperty("storyline") val storyline: String? = null,
@JsonProperty("overview") val overview: String? = null,
@JsonProperty("streams") val streams: ArrayList<Streams>? = arrayListOf(),
)
data class Media(
@JsonProperty("_id") val _id: String? = null,
@JsonProperty("original_title") val original_title: String? = null,
@JsonProperty("title") val title: String? = null,
@JsonProperty("poster_path") val poster_path: String? = null,
@JsonProperty("backdrop_path") val backdrop_path: String? = null,
@JsonProperty("imdb_id") val imdb_id: String? = null,
@JsonProperty("totalEpisodes") val totalEpisodes: String? = null,
@JsonProperty("last_updated_epi") val last_updated_epi: String? = null,
)
data class MediaResponse(
@JsonProperty("movies") val movies: ArrayList<Media>? = arrayListOf(),
)
}