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
|
||||
version = 118
|
||||
version = 119
|
||||
|
||||
|
||||
cloudstream {
|
||||
|
|
|
@ -811,71 +811,62 @@ object SoraExtractor : SoraStream() {
|
|||
|
||||
}
|
||||
|
||||
suspend fun invokeFlixhq(
|
||||
suspend fun invokeFmovies(
|
||||
title: String? = null,
|
||||
year: Int? = null,
|
||||
season: Int? = null,
|
||||
episode: Int? = null,
|
||||
lastSeason: Int? = null,
|
||||
subtitleCallback: (SubtitleFile) -> Unit,
|
||||
callback: (ExtractorLink) -> Unit
|
||||
) {
|
||||
val fixTitle = title?.replace("–", "-")
|
||||
val id = app.get("$haikeiFlixhqAPI/$title")
|
||||
.parsedSafe<ConsumetSearchResponse>()?.results?.find {
|
||||
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 html =
|
||||
app.get("https://fmovies.to/ajax/film/search?vrf=${encodeVrf("$title")}&keyword=$title")
|
||||
.parsedSafe<FmoviesSearch>()?.html
|
||||
|
||||
val episodeId =
|
||||
app.get("$haikeiFlixhqAPI/info?id=$id").parsedSafe<ConsumetDetails>()?.let {
|
||||
if (season == null) {
|
||||
it.episodes?.first()?.id
|
||||
} else {
|
||||
it.episodes?.find { ep -> ep.number == episode && ep.season == season }?.id
|
||||
}
|
||||
} ?: return
|
||||
val mediaId = Jsoup.parse(html ?: return).select("a.item").map {
|
||||
Triple(
|
||||
it.attr("href"),
|
||||
it.select("div.title").text(),
|
||||
it.selectFirst("i.dot")?.nextSibling().toString().trim(),
|
||||
)
|
||||
}.find {
|
||||
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(
|
||||
"vidcloud", "upcloud"
|
||||
).apmap { server ->
|
||||
val sources = app.get(
|
||||
if (server == "upcloud") {
|
||||
"$haikeiFlixhqAPI/watch?episodeId=$episodeId&mediaId=$id"
|
||||
} else {
|
||||
"$haikeiFlixhqAPI/watch?episodeId=$episodeId&mediaId=$id&server=$server"
|
||||
},
|
||||
).parsedSafe<ConsumetSourcesResponse>()
|
||||
val name = fixTitle(server)
|
||||
sources?.sources?.map {
|
||||
callback.invoke(
|
||||
ExtractorLink(
|
||||
name,
|
||||
name,
|
||||
it.url ?: return@map null,
|
||||
sources.headers?.referer ?: "",
|
||||
it.quality?.toIntOrNull() ?: Qualities.Unknown.value,
|
||||
it.isM3U8 ?: true
|
||||
)
|
||||
val episodeId = if (season == null) {
|
||||
"1-full"
|
||||
} else {
|
||||
"$season-$episode"
|
||||
}
|
||||
|
||||
val sources = app.get(
|
||||
"$consumetFmoviesAPI/watch?mediaId=${mediaId.removePrefix("/")}&episodeId=$episodeId"
|
||||
).parsedSafe<ConsumetSourcesResponse>()
|
||||
|
||||
sources?.sources?.map {
|
||||
callback.invoke(
|
||||
ExtractorLink(
|
||||
"Vizcloud",
|
||||
"Vizcloud",
|
||||
it.url ?: return@map null,
|
||||
sources.headers?.referer ?: "",
|
||||
getQualityFromName(it.quality),
|
||||
it.isM3U8 ?: true
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
sources?.subtitles?.map {
|
||||
subtitleCallback.invoke(
|
||||
SubtitleFile(
|
||||
it.lang ?: "", it.url ?: return@map null
|
||||
)
|
||||
sources?.subtitles?.map {
|
||||
subtitleCallback.invoke(
|
||||
SubtitleFile(
|
||||
it.lang ?: "", it.url ?: return@map null
|
||||
)
|
||||
}
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
|
@ -2019,6 +2010,9 @@ object SoraExtractor : SoraStream() {
|
|||
it.first.contains("/gtop") -> {
|
||||
invokeSmashyTwo(it.second, it.first, callback)
|
||||
}
|
||||
it.first.contains("/dude_tv") -> {
|
||||
invokeSmashyThree(it.second, it.first, callback)
|
||||
}
|
||||
else -> return@apmap
|
||||
}
|
||||
}
|
||||
|
@ -3493,3 +3487,12 @@ data class CryMoviesStream(
|
|||
data class CryMoviesResponse(
|
||||
@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.invokeFDMovies
|
||||
import com.hexated.SoraExtractor.invokeFlixon
|
||||
import com.hexated.SoraExtractor.invokeFmovies
|
||||
import com.hexated.SoraExtractor.invokeFwatayako
|
||||
import com.hexated.SoraExtractor.invokeGMovies
|
||||
import com.hexated.SoraExtractor.invokeGdbotMovies
|
||||
|
@ -100,11 +101,11 @@ open class SoraStream : TmdbProvider() {
|
|||
const val filmxyAPI = "https://www.filmxy.vip"
|
||||
const val kimcartoonAPI = "https://kimcartoon.li"
|
||||
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 consumetCrunchyrollAPI = "https://cronchy.consumet.stream" // dead
|
||||
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 uhdmoviesAPI = "https://uhdmovies.vip"
|
||||
const val fwatayakoAPI = "https://5100.svetacdn.in"
|
||||
|
@ -481,17 +482,9 @@ open class SoraStream : TmdbProvider() {
|
|||
callback
|
||||
)
|
||||
},
|
||||
// {
|
||||
// invokeFlixhq(
|
||||
// res.title,
|
||||
// res.year,
|
||||
// res.season,
|
||||
// res.episode,
|
||||
// res.lastSeason,
|
||||
// subtitleCallback,
|
||||
// callback
|
||||
// )
|
||||
// },
|
||||
{
|
||||
invokeFmovies(res.title, res.airedYear ?: res.year, 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.invokeFilmxy
|
||||
import com.hexated.SoraExtractor.invokeFlixon
|
||||
import com.hexated.SoraExtractor.invokeFmovies
|
||||
import com.hexated.SoraExtractor.invokeFwatayako
|
||||
import com.hexated.SoraExtractor.invokeGomovies
|
||||
import com.hexated.SoraExtractor.invokeHDMovieBox
|
||||
|
@ -187,17 +188,16 @@ class SoraStreamLite : SoraStream() {
|
|||
callback
|
||||
)
|
||||
},
|
||||
// {
|
||||
// invokeFlixhq(
|
||||
// res.title,
|
||||
// res.year,
|
||||
// res.season,
|
||||
// res.episode,
|
||||
// res.lastSeason,
|
||||
// subtitleCallback,
|
||||
// callback
|
||||
// )
|
||||
// },
|
||||
{
|
||||
invokeFmovies(
|
||||
res.title,
|
||||
res.airedYear ?: res.year,
|
||||
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>? {
|
||||
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 {
|
||||
|
@ -1113,6 +1133,64 @@ fun getDeviceId(length: Int = 16): String {
|
|||
.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 {
|
||||
val url = URL(this)
|
||||
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)
|
||||
}
|
||||
|
||||
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 {
|
||||
|
||||
|
|
Loading…
Reference in a new issue