mirror of
https://github.com/hexated/cloudstream-extensions-hexated.git
synced 2024-08-15 00:03:22 +00:00
sora: update Smasy and add Vizcloud
This commit is contained in:
parent
753e8a971e
commit
ac2b8b9824
5 changed files with 153 additions and 79 deletions
|
@ -1,5 +1,5 @@
|
||||||
// use an integer for version numbers
|
// use an integer for version numbers
|
||||||
version = 118
|
version = 119
|
||||||
|
|
||||||
|
|
||||||
cloudstream {
|
cloudstream {
|
||||||
|
|
|
@ -811,71 +811,62 @@ object SoraExtractor : SoraStream() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun invokeFlixhq(
|
suspend fun invokeFmovies(
|
||||||
title: String? = null,
|
title: String? = null,
|
||||||
year: Int? = null,
|
year: Int? = null,
|
||||||
season: Int? = null,
|
season: Int? = null,
|
||||||
episode: Int? = null,
|
episode: Int? = null,
|
||||||
lastSeason: Int? = null,
|
|
||||||
subtitleCallback: (SubtitleFile) -> Unit,
|
subtitleCallback: (SubtitleFile) -> Unit,
|
||||||
callback: (ExtractorLink) -> Unit
|
callback: (ExtractorLink) -> Unit
|
||||||
) {
|
) {
|
||||||
val fixTitle = title?.replace("–", "-")
|
val html =
|
||||||
val id = app.get("$haikeiFlixhqAPI/$title")
|
app.get("https://fmovies.to/ajax/film/search?vrf=${encodeVrf("$title")}&keyword=$title")
|
||||||
.parsedSafe<ConsumetSearchResponse>()?.results?.find {
|
.parsedSafe<FmoviesSearch>()?.html
|
||||||
if (season == null) {
|
|
||||||
it.title?.equals(
|
|
||||||
"$fixTitle", true
|
|
||||||
) == true && it.releaseDate?.equals("$year") == true && it.type == "Movie"
|
|
||||||
} else {
|
|
||||||
it.title?.equals(
|
|
||||||
"$fixTitle",
|
|
||||||
true
|
|
||||||
) == true && it.type == "TV Series" && it.seasons == lastSeason
|
|
||||||
}
|
|
||||||
}?.id ?: return
|
|
||||||
|
|
||||||
val episodeId =
|
val mediaId = Jsoup.parse(html ?: return).select("a.item").map {
|
||||||
app.get("$haikeiFlixhqAPI/info?id=$id").parsedSafe<ConsumetDetails>()?.let {
|
Triple(
|
||||||
if (season == null) {
|
it.attr("href"),
|
||||||
it.episodes?.first()?.id
|
it.select("div.title").text(),
|
||||||
} else {
|
it.selectFirst("i.dot")?.nextSibling().toString().trim(),
|
||||||
it.episodes?.find { ep -> ep.number == episode && ep.season == season }?.id
|
)
|
||||||
}
|
}.find {
|
||||||
} ?: return
|
if (season == null) {
|
||||||
|
it.first.contains("/movie/")
|
||||||
|
} else {
|
||||||
|
it.first.contains("/series/")
|
||||||
|
} && (it.second.equals(title, true) || it.second.createSlug()
|
||||||
|
.equals(title.createSlug())) && it.third.toInt() == year
|
||||||
|
}?.first ?: return
|
||||||
|
|
||||||
listOf(
|
val episodeId = if (season == null) {
|
||||||
"vidcloud", "upcloud"
|
"1-full"
|
||||||
).apmap { server ->
|
} else {
|
||||||
val sources = app.get(
|
"$season-$episode"
|
||||||
if (server == "upcloud") {
|
}
|
||||||
"$haikeiFlixhqAPI/watch?episodeId=$episodeId&mediaId=$id"
|
|
||||||
} else {
|
val sources = app.get(
|
||||||
"$haikeiFlixhqAPI/watch?episodeId=$episodeId&mediaId=$id&server=$server"
|
"$consumetFmoviesAPI/watch?mediaId=${mediaId.removePrefix("/")}&episodeId=$episodeId"
|
||||||
},
|
).parsedSafe<ConsumetSourcesResponse>()
|
||||||
).parsedSafe<ConsumetSourcesResponse>()
|
|
||||||
val name = fixTitle(server)
|
sources?.sources?.map {
|
||||||
sources?.sources?.map {
|
callback.invoke(
|
||||||
callback.invoke(
|
ExtractorLink(
|
||||||
ExtractorLink(
|
"Vizcloud",
|
||||||
name,
|
"Vizcloud",
|
||||||
name,
|
it.url ?: return@map null,
|
||||||
it.url ?: return@map null,
|
sources.headers?.referer ?: "",
|
||||||
sources.headers?.referer ?: "",
|
getQualityFromName(it.quality),
|
||||||
it.quality?.toIntOrNull() ?: Qualities.Unknown.value,
|
it.isM3U8 ?: true
|
||||||
it.isM3U8 ?: true
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
)
|
||||||
|
}
|
||||||
|
|
||||||
sources?.subtitles?.map {
|
sources?.subtitles?.map {
|
||||||
subtitleCallback.invoke(
|
subtitleCallback.invoke(
|
||||||
SubtitleFile(
|
SubtitleFile(
|
||||||
it.lang ?: "", it.url ?: return@map null
|
it.lang ?: "", it.url ?: return@map null
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -2019,6 +2010,9 @@ object SoraExtractor : SoraStream() {
|
||||||
it.first.contains("/gtop") -> {
|
it.first.contains("/gtop") -> {
|
||||||
invokeSmashyTwo(it.second, it.first, callback)
|
invokeSmashyTwo(it.second, it.first, callback)
|
||||||
}
|
}
|
||||||
|
it.first.contains("/dude_tv") -> {
|
||||||
|
invokeSmashyThree(it.second, it.first, callback)
|
||||||
|
}
|
||||||
else -> return@apmap
|
else -> return@apmap
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3493,3 +3487,12 @@ data class CryMoviesStream(
|
||||||
data class CryMoviesResponse(
|
data class CryMoviesResponse(
|
||||||
@JsonProperty("streams") val streams: List<CryMoviesStream>? = null,
|
@JsonProperty("streams") val streams: List<CryMoviesStream>? = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
data class DudetvSources(
|
||||||
|
@JsonProperty("file") val file: String? = null,
|
||||||
|
@JsonProperty("title") val title: String? = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
data class FmoviesSearch(
|
||||||
|
@JsonProperty("html") val html: String? = null,
|
||||||
|
)
|
|
@ -28,6 +28,7 @@ import com.hexated.SoraExtractor.invokeDahmerMovies
|
||||||
import com.hexated.SoraExtractor.invokeEdithxmovies
|
import com.hexated.SoraExtractor.invokeEdithxmovies
|
||||||
import com.hexated.SoraExtractor.invokeFDMovies
|
import com.hexated.SoraExtractor.invokeFDMovies
|
||||||
import com.hexated.SoraExtractor.invokeFlixon
|
import com.hexated.SoraExtractor.invokeFlixon
|
||||||
|
import com.hexated.SoraExtractor.invokeFmovies
|
||||||
import com.hexated.SoraExtractor.invokeFwatayako
|
import com.hexated.SoraExtractor.invokeFwatayako
|
||||||
import com.hexated.SoraExtractor.invokeGMovies
|
import com.hexated.SoraExtractor.invokeGMovies
|
||||||
import com.hexated.SoraExtractor.invokeGdbotMovies
|
import com.hexated.SoraExtractor.invokeGdbotMovies
|
||||||
|
@ -100,11 +101,11 @@ open class SoraStream : TmdbProvider() {
|
||||||
const val filmxyAPI = "https://www.filmxy.vip"
|
const val filmxyAPI = "https://www.filmxy.vip"
|
||||||
const val kimcartoonAPI = "https://kimcartoon.li"
|
const val kimcartoonAPI = "https://kimcartoon.li"
|
||||||
const val xMovieAPI = "https://xemovies.to"
|
const val xMovieAPI = "https://xemovies.to"
|
||||||
const val haikeiFlixhqAPI = "https://api.haikei.xyz/movies/flixhq" // disabled due to rate limit
|
const val consumetFmoviesAPI = "https://api.consumet.org/movies/fmovies"
|
||||||
const val consumetZoroAPI = "https://api.consumet.org/anime/zoro"
|
const val consumetZoroAPI = "https://api.consumet.org/anime/zoro"
|
||||||
const val consumetCrunchyrollAPI = "https://cronchy.consumet.stream" // dead
|
const val consumetCrunchyrollAPI = "https://cronchy.consumet.stream" // dead
|
||||||
const val allanimeAPI = "https://api.allanime.to"
|
const val allanimeAPI = "https://api.allanime.to"
|
||||||
const val kissKhAPI = "https://kisskh.me"
|
const val kissKhAPI = "https://kisskh.co"
|
||||||
const val lingAPI = "https://ling-online.net"
|
const val lingAPI = "https://ling-online.net"
|
||||||
const val uhdmoviesAPI = "https://uhdmovies.vip"
|
const val uhdmoviesAPI = "https://uhdmovies.vip"
|
||||||
const val fwatayakoAPI = "https://5100.svetacdn.in"
|
const val fwatayakoAPI = "https://5100.svetacdn.in"
|
||||||
|
@ -481,17 +482,9 @@ open class SoraStream : TmdbProvider() {
|
||||||
callback
|
callback
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
// {
|
{
|
||||||
// invokeFlixhq(
|
invokeFmovies(res.title, res.airedYear ?: res.year, res.season, res.episode, subtitleCallback, callback)
|
||||||
// res.title,
|
},
|
||||||
// res.year,
|
|
||||||
// res.season,
|
|
||||||
// res.episode,
|
|
||||||
// res.lastSeason,
|
|
||||||
// subtitleCallback,
|
|
||||||
// callback
|
|
||||||
// )
|
|
||||||
// },
|
|
||||||
{
|
{
|
||||||
invokeKisskh(res.title, res.season, res.episode, subtitleCallback, callback)
|
invokeKisskh(res.title, res.season, res.episode, subtitleCallback, callback)
|
||||||
},
|
},
|
||||||
|
|
|
@ -7,6 +7,7 @@ import com.hexated.SoraExtractor.invokeCrunchyroll
|
||||||
import com.hexated.SoraExtractor.invokeDbgo
|
import com.hexated.SoraExtractor.invokeDbgo
|
||||||
import com.hexated.SoraExtractor.invokeFilmxy
|
import com.hexated.SoraExtractor.invokeFilmxy
|
||||||
import com.hexated.SoraExtractor.invokeFlixon
|
import com.hexated.SoraExtractor.invokeFlixon
|
||||||
|
import com.hexated.SoraExtractor.invokeFmovies
|
||||||
import com.hexated.SoraExtractor.invokeFwatayako
|
import com.hexated.SoraExtractor.invokeFwatayako
|
||||||
import com.hexated.SoraExtractor.invokeGomovies
|
import com.hexated.SoraExtractor.invokeGomovies
|
||||||
import com.hexated.SoraExtractor.invokeHDMovieBox
|
import com.hexated.SoraExtractor.invokeHDMovieBox
|
||||||
|
@ -187,17 +188,16 @@ class SoraStreamLite : SoraStream() {
|
||||||
callback
|
callback
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
// {
|
{
|
||||||
// invokeFlixhq(
|
invokeFmovies(
|
||||||
// res.title,
|
res.title,
|
||||||
// res.year,
|
res.airedYear ?: res.year,
|
||||||
// res.season,
|
res.season,
|
||||||
// res.episode,
|
res.episode,
|
||||||
// res.lastSeason,
|
subtitleCallback,
|
||||||
// subtitleCallback,
|
callback
|
||||||
// callback
|
)
|
||||||
// )
|
},
|
||||||
// },
|
|
||||||
{
|
{
|
||||||
invokeKisskh(res.title, res.season, res.episode, subtitleCallback, callback)
|
invokeKisskh(res.title, res.season, res.episode, subtitleCallback, callback)
|
||||||
},
|
},
|
||||||
|
|
|
@ -460,6 +460,26 @@ suspend fun invokeSmashyTwo(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun invokeSmashyThree(
|
||||||
|
name: String,
|
||||||
|
url: String,
|
||||||
|
callback: (ExtractorLink) -> Unit
|
||||||
|
) {
|
||||||
|
val script =
|
||||||
|
app.get(url).document.selectFirst("script:containsData(player =)")?.data() ?: return
|
||||||
|
|
||||||
|
val source = Regex("file:\\s*(\\[.*]),").find(script)?.groupValues?.get(1) ?: return
|
||||||
|
|
||||||
|
tryParseJson<ArrayList<DudetvSources>>(source)?.filter { it.title == "English" }?.map {
|
||||||
|
M3u8Helper.generateM3u8(
|
||||||
|
"Smashy [Player 2]",
|
||||||
|
it.file ?: return@map ,
|
||||||
|
""
|
||||||
|
).forEach(callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun getSoraIdAndType(title: String?, year: Int?, season: Int?) : Pair<String, String>? {
|
suspend fun getSoraIdAndType(title: String?, year: Int?, season: Int?) : Pair<String, String>? {
|
||||||
val doc = app.get("${base64DecodeAPI("b20=LmM=b2s=a2w=bG8=Ly8=czo=dHA=aHQ=")}/search?keyword=$title").document
|
val doc = app.get("${base64DecodeAPI("b20=LmM=b2s=a2w=bG8=Ly8=czo=dHA=aHQ=")}/search?keyword=$title").document
|
||||||
val scriptData = doc.select("div.search-list div.search-video-card").map {
|
val scriptData = doc.select("div.search-list div.search-video-card").map {
|
||||||
|
@ -1113,6 +1133,64 @@ fun getDeviceId(length: Int = 16): String {
|
||||||
.joinToString("")
|
.joinToString("")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun encodeVrf(query: String): String {
|
||||||
|
return encode(
|
||||||
|
encryptVrf(
|
||||||
|
cipherVrf("DZmuZuXqa9O0z3b7", encode(query)),
|
||||||
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun encryptVrf(input: String, key: String): String {
|
||||||
|
if (input.any { it.code > 255 }) throw Exception("illegal characters!")
|
||||||
|
var output = ""
|
||||||
|
for (i in input.indices step 3) {
|
||||||
|
val a = intArrayOf(-1, -1, -1, -1)
|
||||||
|
a[0] = input[i].code shr 2
|
||||||
|
a[1] = (3 and input[i].code) shl 4
|
||||||
|
if (input.length > i + 1) {
|
||||||
|
a[1] = a[1] or (input[i + 1].code shr 4)
|
||||||
|
a[2] = (15 and input[i + 1].code) shl 2
|
||||||
|
}
|
||||||
|
if (input.length > i + 2) {
|
||||||
|
a[2] = a[2] or (input[i + 2].code shr 6)
|
||||||
|
a[3] = 63 and input[i + 2].code
|
||||||
|
}
|
||||||
|
for (n in a) {
|
||||||
|
if (n == -1) output += "="
|
||||||
|
else {
|
||||||
|
if (n in 0..63) output += key[n]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return output
|
||||||
|
}
|
||||||
|
|
||||||
|
fun cipherVrf(key: String, text: String): String {
|
||||||
|
val arr = IntArray(256) { it }
|
||||||
|
|
||||||
|
var u = 0
|
||||||
|
var r: Int
|
||||||
|
arr.indices.forEach {
|
||||||
|
u = (u + arr[it] + key[it % key.length].code) % 256
|
||||||
|
r = arr[it]
|
||||||
|
arr[it] = arr[u]
|
||||||
|
arr[u] = r
|
||||||
|
}
|
||||||
|
u = 0
|
||||||
|
var c = 0
|
||||||
|
|
||||||
|
return text.indices.map { j ->
|
||||||
|
c = (c + 1) % 256
|
||||||
|
u = (u + arr[c]) % 256
|
||||||
|
r = arr[c]
|
||||||
|
arr[c] = arr[u]
|
||||||
|
arr[u] = r
|
||||||
|
(text[j].code xor arr[(arr[c] + arr[u]) % 256]).toChar()
|
||||||
|
}.joinToString("")
|
||||||
|
}
|
||||||
|
|
||||||
fun String.encodeUrl(): String {
|
fun String.encodeUrl(): String {
|
||||||
val url = URL(this)
|
val url = URL(this)
|
||||||
val uri = URI(url.protocol, url.userInfo, url.host, url.port, url.path, url.query, url.ref)
|
val uri = URI(url.protocol, url.userInfo, url.host, url.port, url.path, url.query, url.ref)
|
||||||
|
@ -1129,7 +1207,7 @@ fun String.decodeBase64(): String {
|
||||||
return Base64.decode(this, Base64.DEFAULT).toString(Charsets.UTF_8)
|
return Base64.decode(this, Base64.DEFAULT).toString(Charsets.UTF_8)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun encode(input: String): String? = URLEncoder.encode(input, "utf-8")
|
fun encode(input: String): String = URLEncoder.encode(input, "utf-8").replace("+", "%20")
|
||||||
|
|
||||||
fun decryptStreamUrl(data: String): String {
|
fun decryptStreamUrl(data: String): String {
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue