mirror of
https://github.com/hexated/cloudstream-extensions-hexated.git
synced 2024-08-15 00:03:22 +00:00
added Xcineio
This commit is contained in:
parent
33d397c6fd
commit
be027dce8d
5 changed files with 349 additions and 0 deletions
26
Xcineio/build.gradle.kts
Normal file
26
Xcineio/build.gradle.kts
Normal file
|
@ -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%"
|
||||||
|
}
|
2
Xcineio/src/main/AndroidManifest.xml
Normal file
2
Xcineio/src/main/AndroidManifest.xml
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest package="com.hexated"/>
|
238
Xcineio/src/main/kotlin/com/hexated/Xcineio.kt
Normal file
238
Xcineio/src/main/kotlin/com/hexated/Xcineio.kt
Normal file
|
@ -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<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("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<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
|
||||||
|
// 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<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 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<Streams>? = 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<String>? = arrayListOf(),
|
||||||
|
// @JsonProperty("credits") val credits: ArrayList<Credits>? = arrayListOf(),
|
||||||
|
@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(),
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
63
Xcineio/src/main/kotlin/com/hexated/XcineioExtractors.kt
Normal file
63
Xcineio/src/main/kotlin/com/hexated/XcineioExtractors.kt
Normal file
|
@ -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
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
20
Xcineio/src/main/kotlin/com/hexated/XcineioPlugin.kt
Normal file
20
Xcineio/src/main/kotlin/com/hexated/XcineioPlugin.kt
Normal file
|
@ -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())
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue