From be027dce8dd6824d8af4cb4ae7e7c7c8cb83450e Mon Sep 17 00:00:00 2001 From: hexated Date: Wed, 16 Nov 2022 22:37:58 +0700 Subject: [PATCH] added Xcineio --- Xcineio/build.gradle.kts | 26 ++ Xcineio/src/main/AndroidManifest.xml | 2 + .../src/main/kotlin/com/hexated/Xcineio.kt | 238 ++++++++++++++++++ .../kotlin/com/hexated/XcineioExtractors.kt | 63 +++++ .../main/kotlin/com/hexated/XcineioPlugin.kt | 20 ++ 5 files changed, 349 insertions(+) create mode 100644 Xcineio/build.gradle.kts create mode 100644 Xcineio/src/main/AndroidManifest.xml create mode 100644 Xcineio/src/main/kotlin/com/hexated/Xcineio.kt create mode 100644 Xcineio/src/main/kotlin/com/hexated/XcineioExtractors.kt create mode 100644 Xcineio/src/main/kotlin/com/hexated/XcineioPlugin.kt diff --git a/Xcineio/build.gradle.kts b/Xcineio/build.gradle.kts new file mode 100644 index 00000000..a002b630 --- /dev/null +++ b/Xcineio/build.gradle.kts @@ -0,0 +1,26 @@ +// use an integer for version numbers +version = 1 + + +cloudstream { + language = "de" + // All of these properties are optional, you can safely remove them + + // description = "Lorem Ipsum" + authors = listOf("Hexated") + + /** + * Status int as the following: + * 0: Down + * 1: Ok + * 2: Slow + * 3: Beta only + * */ + status = 1 // will be 3 if unspecified + tvTypes = listOf( + "TvSeries", + "Movie", + ) + + iconUrl = "https://www.google.com/s2/favicons?domain=www5.xcine.io&sz=%size%" +} \ No newline at end of file diff --git a/Xcineio/src/main/AndroidManifest.xml b/Xcineio/src/main/AndroidManifest.xml new file mode 100644 index 00000000..c98063f8 --- /dev/null +++ b/Xcineio/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/Xcineio/src/main/kotlin/com/hexated/Xcineio.kt b/Xcineio/src/main/kotlin/com/hexated/Xcineio.kt new file mode 100644 index 00000000..916664d8 --- /dev/null +++ b/Xcineio/src/main/kotlin/com/hexated/Xcineio.kt @@ -0,0 +1,238 @@ +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("Try Again") + val type = if (res.tv == 1) "tv" else "movie" +// val actors = res.credits?.map { cast -> +// ActorData( +// Actor( +// cast.name ?: return null, +// getImageUrl(cast.profile_path) +// ), +// roleString = cast.character +// ) +// } ?: res.cast?.map { cast -> +// ActorData( +// Actor(cast) +// ) +// } + + 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 +// this.actors = actors + } + } 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 +// this.actors = actors + } + } + + } + + 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 Data( +// val id: 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 Credits( +// @JsonProperty("name") val name: String? = null, +// @JsonProperty("character") val character: String? = null, +// @JsonProperty("profile_path") val profile_path: String? = null, +// ) + + 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("cast") val cast: ArrayList? = arrayListOf(), +// @JsonProperty("credits") val credits: ArrayList? = arrayListOf(), + @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(), + ) + +} \ No newline at end of file diff --git a/Xcineio/src/main/kotlin/com/hexated/XcineioExtractors.kt b/Xcineio/src/main/kotlin/com/hexated/XcineioExtractors.kt new file mode 100644 index 00000000..a19ac8a0 --- /dev/null +++ b/Xcineio/src/main/kotlin/com/hexated/XcineioExtractors.kt @@ -0,0 +1,63 @@ +package com.hexated + +import com.lagradost.cloudstream3.SubtitleFile +import com.lagradost.cloudstream3.app +import com.lagradost.cloudstream3.extractors.DoodLaExtractor +import com.lagradost.cloudstream3.extractors.MixDrop +import com.lagradost.cloudstream3.extractors.StreamTape +import com.lagradost.cloudstream3.utils.ExtractorApi +import com.lagradost.cloudstream3.utils.ExtractorLink +import com.lagradost.cloudstream3.utils.Qualities +import com.lagradost.cloudstream3.utils.getAndUnpack + + +class StreamTapeAdblockuser : StreamTape() { + override var mainUrl = "https://streamtapeadblockuser.xyz" +} + +class StreamTapeTo : StreamTape() { + override var mainUrl = "https://streamtape.to" +} + +class Mixdrp : MixDrop(){ + override var mainUrl = "https://mixdrp.to" +} + +class DoodReExtractor : DoodLaExtractor() { + override var mainUrl = "https://dood.re" +} + +class Streamzz : Streamcrypt() { + override var name = "Streamzz" + override var mainUrl = "https://streamzz.to" +} + +open class Streamcrypt : ExtractorApi() { + override var name = "Streamcrypt" + override var mainUrl = "https://streamcrypt.net" + override val requiresReferer = false + + override suspend fun getUrl( + url: String, + referer: String?, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ) { + val doc = app.get(url).document + doc.select("script").map { it.data() }.filter { it.contains("eval(function(p,a,c,k,e,d)") } + .map { script -> + val data = getAndUnpack(script) + val link = Regex("src:['\"](.*)['\"]").find(data)?.groupValues?.getOrNull(1) + ?: return@map null + callback.invoke( + ExtractorLink( + this.name, + this.name, + link, + "", + Qualities.Unknown.value + ) + ) + } + } +} \ No newline at end of file diff --git a/Xcineio/src/main/kotlin/com/hexated/XcineioPlugin.kt b/Xcineio/src/main/kotlin/com/hexated/XcineioPlugin.kt new file mode 100644 index 00000000..7d2fc3d8 --- /dev/null +++ b/Xcineio/src/main/kotlin/com/hexated/XcineioPlugin.kt @@ -0,0 +1,20 @@ + +package com.hexated + +import com.lagradost.cloudstream3.plugins.CloudstreamPlugin +import com.lagradost.cloudstream3.plugins.Plugin +import android.content.Context + +@CloudstreamPlugin +class XcineioPlugin: Plugin() { + override fun load(context: Context) { + // All providers should be added in this manner. Please don't edit the providers list directly. + registerMainAPI(Xcineio()) + registerExtractorAPI(StreamTapeAdblockuser()) + registerExtractorAPI(StreamTapeTo()) + registerExtractorAPI(Mixdrp()) + registerExtractorAPI(DoodReExtractor()) + registerExtractorAPI(Streamzz()) + registerExtractorAPI(Streamcrypt()) + } +} \ No newline at end of file