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 open class XCine : MainAPI() { override var name = "XCine" override var mainUrl = "https://www1.xcine.ru" override var lang = "de" override val hasQuickSearch = true override val usesWebView = false override val hasMainPage = true override val supportedTypes = setOf(TvType.TvSeries, TvType.Movie) open var mainAPI = "https://api.xcine.ru" override val mainPage = mainPageOf( "data/browse/?lang=2&keyword=&year=&rating=&votes=&genre=&country=&cast=&directors=&type=movies&order_by=trending" to "Trending", "data/browse/?lang=2&keyword=&year=&rating=&votes=&genre=&country=&cast=&directors=&type=movies&order_by=Views" to "Most View Filme", "data/browse/?lang=2&keyword=&year=&rating=&votes=&genre=&country=&cast=&directors=&type=tvseries&order_by=Trending" to "Trending Serien", "data/browse/?lang=2&keyword=&year=&rating=&votes=&genre=&country=&cast=&directors=&type=movies&order_by=Updates" to "Updated Filme", "data/browse/?lang=2&keyword=&year=&rating=&votes=&genre=&country=&cast=&directors=&type=tvseries&order_by=Updates" 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 } private fun getBackupImageUrl(link: String?): String? { if (link == null) return null return "https://cdn.movie4k.stream/data${link.substringAfter("/data")}" } override suspend fun getMainPage( page: Int, request: MainPageRequest ): HomePageResponse { val home = app.get("$mainAPI/${request.data}&page=$page", referer = "$mainUrl/") .parsedSafe()?.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(), Link(id=_id).toJson(), TvType.TvSeries, false ) { this.posterUrl = getImageUrl(poster_path ?: backdrop_path) ?: getBackupImageUrl(img) addDub(last_updated_epi?.toIntOrNull()) addSub(totalEpisodes?.toIntOrNull()) } } override suspend fun quickSearch(query: String): List = search(query) override suspend fun search(query: String): List { val res = app.get("$mainAPI/data/search/?lang=2&keyword=$query", referer = "$mainUrl/").text return tryParseJson>(res)?.mapNotNull { it.toSearchResponse() } ?: throw ErrorLoadingException() } override suspend fun load(url: String): LoadResponse? { val id = parseJson(url).id val res = app.get("$mainAPI/data/watch/?_id=$id", referer = "$mainUrl/") .parsedSafe() ?: 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>(it) }?.mapNotNull { it.toSearchResponse() } return if (type == "tv") { val episodes = res.streams?.groupBy { it.e.toString().toIntOrNull() }?.mapNotNull { eps -> val epsNum = eps.key val epsLink = eps.value.map { it.stream }.toJson() Episode(epsLink, episode = epsNum) } ?: emptyList() newTvSeriesLoadResponse( res.title ?: res.original_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>(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? = null, val id: String? = null, ) 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? = arrayListOf(), ) data class Streams( @JsonProperty("_id") val _id: String? = null, @JsonProperty("stream") val stream: String? = null, @JsonProperty("e") val e: Any? = 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? = 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("img") val img: 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? = arrayListOf(), ) }