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 Xcineio : MainAPI() { override var name = "Xcine.io" override var mainUrl = "https://www5.xcine.io" 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.io" 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()?.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 = search(query) override suspend fun search(query: String): List { 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()?.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() ?: 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 = mutableListOf() val json = app.get("$mainAPI/data/seasons/?lang=2&original_title=${res.original_title}").text.let { tryParseJson>(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>(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? = 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? = 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? = arrayListOf(), ) }