mirror of
https://github.com/hexated/cloudstream-extensions-hexated.git
synced 2024-08-15 00:03:22 +00:00
[Sora:Test] Adding FDMovies
This commit is contained in:
parent
89116dc209
commit
df4178f088
4 changed files with 144 additions and 107 deletions
|
@ -1,5 +1,5 @@
|
|||
// use an integer for version numbers
|
||||
version = 45
|
||||
version = 46
|
||||
|
||||
|
||||
cloudstream {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.hexated
|
||||
|
||||
import android.util.Log
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import com.lagradost.cloudstream3.*
|
||||
import com.lagradost.cloudstream3.utils.*
|
||||
|
@ -1354,47 +1355,64 @@ object SoraExtractor : SoraStream() {
|
|||
})?.filter { it.first.contains("gdtot") } ?: return
|
||||
|
||||
iframe.apmap { (iframeLink, title) ->
|
||||
val size = Regex("(?i)\\s(\\S+gb|mb)").find(title)?.groupValues?.getOrNull(1)
|
||||
val gdBotLink = extractGdbot(iframeLink)
|
||||
val iframeGdbot = app.get(
|
||||
gdBotLink ?: return@apmap null
|
||||
).document.selectFirst("ul.divide-y li.flex.flex-col a:contains(Drivebot)")
|
||||
?.attr("href")
|
||||
val driveDoc = app.get(iframeGdbot ?: return@apmap null)
|
||||
val videoLink = extractDrivebot(gdBotLink ?: return@apmap null)
|
||||
|
||||
val ssid = driveDoc.cookies["PHPSESSID"]
|
||||
val script = driveDoc.document.selectFirst("script:containsData(var formData)")?.data()
|
||||
callback.invoke(
|
||||
ExtractorLink(
|
||||
"GMovies [$size]",
|
||||
"GMovies [$size]",
|
||||
videoLink ?: return@apmap null,
|
||||
"",
|
||||
getGMoviesQuality(title)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val baseUrl = getBaseUrl(iframeGdbot)
|
||||
val token = script?.substringAfter("'token', '")?.substringBefore("');")
|
||||
val link = script?.substringAfter("fetch('")?.substringBefore("',").let { "$baseUrl$it" }
|
||||
suspend fun invokeFDMovies(
|
||||
title: String? = null,
|
||||
season: Int? = null,
|
||||
episode: Int? = null,
|
||||
subtitleCallback: (SubtitleFile) -> Unit,
|
||||
callback: (ExtractorLink) -> Unit
|
||||
) {
|
||||
val fixTitle = title.fixTitle()
|
||||
val url = if (season == null) {
|
||||
"$fdMoviesAPI/movies/$fixTitle"
|
||||
} else {
|
||||
"$fdMoviesAPI/episodes/$fixTitle-s${season}xe${episode}/"
|
||||
}
|
||||
|
||||
val body = FormBody.Builder()
|
||||
.addEncoded("token", "$token")
|
||||
.build()
|
||||
val cookies = mapOf("PHPSESSID" to "$ssid")
|
||||
val request = app.get(url)
|
||||
if(!request.isSuccessful) return
|
||||
|
||||
val size = Regex("(?i)\\s(\\S+gb|mb)").find(title)?.groupValues?.getOrNull(1)?.let { "[$it]" } ?: ""
|
||||
app.post(
|
||||
link,
|
||||
requestBody = body,
|
||||
headers = mapOf(
|
||||
"Accept" to "*/*",
|
||||
"Origin" to baseUrl,
|
||||
"Sec-Fetch-Site" to "same-origin"
|
||||
),
|
||||
cookies = cookies
|
||||
).parsedSafe<GdBotLink>()?.url?.let { videoLink ->
|
||||
callback.invoke(
|
||||
ExtractorLink(
|
||||
"GMovies $size",
|
||||
"GMovies $size",
|
||||
videoLink,
|
||||
"",
|
||||
getGMoviesQuality(title)
|
||||
)
|
||||
val iframe = request.document.select("div#download tbody tr").map { it }
|
||||
.filter { it.select("img").attr("src").contains("gdtot") }.map {
|
||||
Triple(
|
||||
it.select("a").attr("href"),
|
||||
it.select("strong.quality").text(),
|
||||
it.select("td:nth-child(4)").text()
|
||||
)
|
||||
}
|
||||
Log.i("fdMoviesAPI", "$iframe")
|
||||
iframe.apmap { (link, quality, size) ->
|
||||
val fdLink = bypassFdAds(link)
|
||||
val gdBotLink = extractGdbot(fdLink ?: return@apmap null)
|
||||
val videoLink = extractDrivebot(gdBotLink ?: return@apmap null)
|
||||
|
||||
callback.invoke(
|
||||
ExtractorLink(
|
||||
"FDMovies [$size]",
|
||||
"FDMovies [$size]",
|
||||
videoLink ?: return@apmap null,
|
||||
"",
|
||||
getGMoviesQuality(quality)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -1548,4 +1566,8 @@ data class SeasonFwatayako(
|
|||
data class SourcesFwatayako(
|
||||
@JsonProperty("movie") val sourcesMovie: String? = null,
|
||||
@JsonProperty("tv") val sourcesTv: ArrayList<SeasonFwatayako>? = arrayListOf(),
|
||||
)
|
||||
|
||||
data class DriveBotLink(
|
||||
@JsonProperty("url") val url: String? = null,
|
||||
)
|
|
@ -11,7 +11,6 @@ import com.hexated.SoraExtractor.invokeIdlix
|
|||
import com.hexated.SoraExtractor.invokeKimcartoon
|
||||
import com.hexated.SoraExtractor.invokeMovieHab
|
||||
import com.hexated.SoraExtractor.invokeNoverse
|
||||
import com.hexated.SoraExtractor.invokeOlgply
|
||||
import com.hexated.SoraExtractor.invokeSeries9
|
||||
import com.hexated.SoraExtractor.invokeSoraVIP
|
||||
import com.hexated.SoraExtractor.invokeTwoEmbed
|
||||
|
@ -22,6 +21,7 @@ import com.lagradost.cloudstream3.*
|
|||
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
|
||||
import com.lagradost.cloudstream3.metaproviders.TmdbProvider
|
||||
import com.hexated.SoraExtractor.invoZoro
|
||||
import com.hexated.SoraExtractor.invokeFDMovies
|
||||
import com.hexated.SoraExtractor.invokeFwatayako
|
||||
import com.hexated.SoraExtractor.invokeGMovies
|
||||
import com.hexated.SoraExtractor.invokeLing
|
||||
|
@ -52,7 +52,7 @@ open class SoraStream : TmdbProvider() {
|
|||
const val gdbot = "https://gdbot.xyz"
|
||||
|
||||
private val mainAPI = base64DecodeAPI("cHA=LmE=ZWw=cmM=dmU=aC4=dGM=d2E=eHA=Ly8=czo=dHA=aHQ=")
|
||||
private var mainServerAPI = base64DecodeAPI("cA==YXA=bC4=Y2U=ZXI=LnY=aWU=b3Y=LW0=cmE=c28=Ly8=czo=dHA=aHQ=")
|
||||
// private var mainServerAPI = base64DecodeAPI("cA==YXA=bC4=Y2U=ZXI=LnY=aWU=b3Y=LW0=cmE=c28=Ly8=czo=dHA=aHQ=")
|
||||
const val twoEmbedAPI = "https://www.2embed.to"
|
||||
const val vidSrcAPI = "https://v2.vidsrc.me"
|
||||
const val dbgoAPI = "https://dbgo.fun"
|
||||
|
@ -75,6 +75,7 @@ open class SoraStream : TmdbProvider() {
|
|||
const val uhdmoviesAPI = "https://uhdmovies.site"
|
||||
const val fwatayakoAPI = "https://5100.svetacdn.in"
|
||||
const val gMoviesAPI = "https://gdrivemovies.xyz"
|
||||
const val fdMoviesAPI = "https://freedrivemovie.com"
|
||||
|
||||
fun getType(t: String?): TvType {
|
||||
return when (t) {
|
||||
|
@ -166,12 +167,13 @@ open class SoraStream : TmdbProvider() {
|
|||
override suspend fun load(url: String): LoadResponse? {
|
||||
val data = parseJson<Data>(url)
|
||||
val type = getType(data.type)
|
||||
val resUrl = if(type == TvType.Movie) {
|
||||
val resUrl = if (type == TvType.Movie) {
|
||||
"$tmdbAPI/movie/${data.id}?api_key=$apiKey&append_to_response=credits,external_ids,videos,recommendations"
|
||||
} else {
|
||||
"$tmdbAPI/tv/${data.id}?api_key=$apiKey&append_to_response=credits,external_ids,videos,recommendations"
|
||||
}
|
||||
val res = app.get(resUrl).parsedSafe<MediaDetail>() ?: throw ErrorLoadingException("Invalid Json Response")
|
||||
val res = app.get(resUrl).parsedSafe<MediaDetail>()
|
||||
?: throw ErrorLoadingException("Invalid Json Response")
|
||||
|
||||
val title = res.title ?: res.name ?: return null
|
||||
val poster = getOriImageUrl(res.posterPath)
|
||||
|
@ -180,7 +182,7 @@ open class SoraStream : TmdbProvider() {
|
|||
val year = (res.releaseDate ?: res.firstAirDate)?.split("-")?.first()?.toIntOrNull()
|
||||
val rating = res.vote_average.toString().toRatingInt()
|
||||
val genres = res.genres?.mapNotNull { it.name }
|
||||
val show = if (genres?.contains("Animation") == true && res.original_language == "ja") "Anime" else "Series/Movies"
|
||||
val isAnime = genres?.contains("Animation") == true && res.original_language == "ja"
|
||||
|
||||
val actors = res.credits?.cast?.mapNotNull { cast ->
|
||||
ActorData(
|
||||
|
@ -191,7 +193,8 @@ open class SoraStream : TmdbProvider() {
|
|||
roleString = cast.character
|
||||
)
|
||||
} ?: return null
|
||||
val recommendations = res.recommendations?.results?.mapNotNull { media -> media.toSearchResponse() }
|
||||
val recommendations =
|
||||
res.recommendations?.results?.mapNotNull { media -> media.toSearchResponse() }
|
||||
|
||||
val trailer = res.videos?.results?.map { "https://www.youtube.com/watch?v=${it.key}" }
|
||||
?.randomOrNull()
|
||||
|
@ -212,7 +215,7 @@ open class SoraStream : TmdbProvider() {
|
|||
title = title,
|
||||
year = season.airDate?.split("-")?.first()?.toIntOrNull(),
|
||||
orgTitle = orgTitle,
|
||||
show = show,
|
||||
isAnime = isAnime,
|
||||
airedYear = year,
|
||||
lastSeason = lastSeason
|
||||
).toJson(),
|
||||
|
@ -256,7 +259,7 @@ open class SoraStream : TmdbProvider() {
|
|||
title = title,
|
||||
year = year,
|
||||
orgTitle = orgTitle,
|
||||
show = show,
|
||||
isAnime = isAnime,
|
||||
).toJson(),
|
||||
) {
|
||||
this.posterUrl = poster
|
||||
|
@ -280,16 +283,6 @@ open class SoraStream : TmdbProvider() {
|
|||
): Boolean {
|
||||
|
||||
val res = parseJson<LinkData>(data)
|
||||
// val query = if (res.type == "tv") {
|
||||
// "$mainServerAPI/tv-shows/${res.id}/season/${res.season}/episode/${res.episode}?_data=routes/tv-shows/\$tvId.season.\$seasonId.episode.\$episodeId"
|
||||
// } else {
|
||||
// "$mainServerAPI/movies/${res.id}/watch?_data=routes/movies/\$movieId.watch"
|
||||
// }
|
||||
// val referer = if (res.type == "tv") {
|
||||
// "$mainServerAPI/tv-shows/${res.id}/season/${res.season}/episode/${res.episode}"
|
||||
// } else {
|
||||
// "$mainServerAPI/movies/${res.id}/watch"
|
||||
// }
|
||||
|
||||
argamap(
|
||||
{
|
||||
|
@ -302,35 +295,6 @@ open class SoraStream : TmdbProvider() {
|
|||
callback
|
||||
)
|
||||
},
|
||||
// {
|
||||
// val json = app.get(
|
||||
// query,
|
||||
// referer = referer,
|
||||
// headers = mapOf("User-Agent" to getRandomUserAgent())
|
||||
// ).parsedSafe<LoadLinks>()
|
||||
//
|
||||
// json?.sources?.map { source ->
|
||||
// callback.invoke(
|
||||
// ExtractorLink(
|
||||
// this.name,
|
||||
// this.name,
|
||||
// source.url ?: return@map null,
|
||||
// "$mainServerAPI/",
|
||||
// source.quality?.toIntOrNull() ?: Qualities.Unknown.value,
|
||||
// isM3u8 = source.isM3U8,
|
||||
// headers = mapOf("Origin" to mainServerAPI)
|
||||
// )
|
||||
// )
|
||||
// }
|
||||
// json?.subtitles?.map { sub ->
|
||||
// subtitleCallback.invoke(
|
||||
// SubtitleFile(
|
||||
// getLanguage(sub.lang.toString()),
|
||||
// sub.url ?: return@map null
|
||||
// )
|
||||
// )
|
||||
// }
|
||||
// },
|
||||
{
|
||||
invokeTwoEmbed(res.id, res.season, res.episode, subtitleCallback, callback)
|
||||
},
|
||||
|
@ -366,7 +330,7 @@ open class SoraStream : TmdbProvider() {
|
|||
// )
|
||||
// },
|
||||
{
|
||||
if (res.season != null && res.show == "Anime") invoZoro(
|
||||
if (res.season != null && res.isAnime) invoZoro(
|
||||
res.id,
|
||||
res.season,
|
||||
res.episode,
|
||||
|
@ -375,7 +339,7 @@ open class SoraStream : TmdbProvider() {
|
|||
)
|
||||
},
|
||||
{
|
||||
invokeHDMovieBox(res.title, res.season, res.episode, callback)
|
||||
if(!res.isAnime) invokeHDMovieBox(res.title, res.season, res.episode, callback)
|
||||
},
|
||||
{
|
||||
invokeSeries9(res.title, res.season, res.episode, subtitleCallback, callback)
|
||||
|
@ -443,7 +407,7 @@ open class SoraStream : TmdbProvider() {
|
|||
)
|
||||
},
|
||||
{
|
||||
invokeUhdmovies(
|
||||
if(!res.isAnime) invokeUhdmovies(
|
||||
res.title,
|
||||
res.year,
|
||||
res.season,
|
||||
|
@ -457,7 +421,10 @@ open class SoraStream : TmdbProvider() {
|
|||
invokeFwatayako(res.imdbId, res.season, res.episode, subtitleCallback, callback)
|
||||
},
|
||||
{
|
||||
invokeGMovies(res.title, res.year, res.season, res.episode, subtitleCallback, callback)
|
||||
if(!res.isAnime) invokeGMovies(res.title, res.year, res.season, res.episode, subtitleCallback, callback)
|
||||
},
|
||||
{
|
||||
if(!res.isAnime) invokeFDMovies(res.title, res.season, res.episode, subtitleCallback, callback)
|
||||
},
|
||||
)
|
||||
|
||||
|
@ -475,7 +442,7 @@ open class SoraStream : TmdbProvider() {
|
|||
val title: String? = null,
|
||||
val year: Int? = null,
|
||||
val orgTitle: String? = null,
|
||||
val show: String? = null,
|
||||
val isAnime: Boolean = false,
|
||||
val airedYear: Int? = null,
|
||||
val lastSeason: Int? = null,
|
||||
)
|
||||
|
@ -487,23 +454,6 @@ open class SoraStream : TmdbProvider() {
|
|||
val malId: Int? = null,
|
||||
)
|
||||
|
||||
data class Subtitles(
|
||||
@JsonProperty("url") val url: String? = null,
|
||||
@JsonProperty("lang") val lang: String? = null,
|
||||
@JsonProperty("language") val language: String? = null,
|
||||
)
|
||||
|
||||
data class Sources(
|
||||
@JsonProperty("url") val url: String? = null,
|
||||
@JsonProperty("quality") val quality: String? = null,
|
||||
@JsonProperty("isM3U8") val isM3U8: Boolean = true,
|
||||
)
|
||||
|
||||
data class LoadLinks(
|
||||
@JsonProperty("sources") val sources: ArrayList<Sources>? = arrayListOf(),
|
||||
@JsonProperty("subtitles") val subtitles: ArrayList<Subtitles>? = arrayListOf(),
|
||||
)
|
||||
|
||||
data class Results(
|
||||
@JsonProperty("results") val results: ArrayList<Media>? = arrayListOf(),
|
||||
)
|
||||
|
|
|
@ -5,10 +5,12 @@ import com.lagradost.cloudstream3.app
|
|||
import com.lagradost.cloudstream3.base64Decode
|
||||
import com.lagradost.cloudstream3.base64Encode
|
||||
import com.lagradost.cloudstream3.network.WebViewResolver
|
||||
import com.lagradost.cloudstream3.utils.AppUtils
|
||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||
import com.lagradost.cloudstream3.utils.Qualities
|
||||
import com.lagradost.cloudstream3.utils.getQualityFromName
|
||||
import com.lagradost.nicehttp.requestCreator
|
||||
import okhttp3.FormBody
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import java.net.URI
|
||||
|
||||
|
@ -62,10 +64,12 @@ suspend fun extractGdbot(url: String): String? {
|
|||
)
|
||||
val token = res.document.selectFirst("input[name=_token]")?.attr("value")
|
||||
val cookiesSet = res.headers.filter { it.first == "set-cookie" }
|
||||
val xsrf = cookiesSet.find { it.second.contains("XSRF-TOKEN") }?.second?.substringAfter("XSRF-TOKEN=")
|
||||
?.substringBefore(";")
|
||||
val session = cookiesSet.find { it.second.contains("gdtot_proxy_session") }?.second?.substringAfter("gdtot_proxy_session=")
|
||||
?.substringBefore(";")
|
||||
val xsrf =
|
||||
cookiesSet.find { it.second.contains("XSRF-TOKEN") }?.second?.substringAfter("XSRF-TOKEN=")
|
||||
?.substringBefore(";")
|
||||
val session =
|
||||
cookiesSet.find { it.second.contains("gdtot_proxy_session") }?.second?.substringAfter("gdtot_proxy_session=")
|
||||
?.substringBefore(";")
|
||||
|
||||
val cookies = mapOf(
|
||||
"gdtot_proxy_session" to "$session",
|
||||
|
@ -81,6 +85,67 @@ suspend fun extractGdbot(url: String): String? {
|
|||
return requestFile.selectFirst("div.mt-8 a.float-right")?.attr("href")
|
||||
}
|
||||
|
||||
suspend fun extractDrivebot(url: String): String? {
|
||||
val iframeGdbot =
|
||||
app.get(url).document.selectFirst("li.flex.flex-col.py-6 a:contains(Drivebot)")
|
||||
?.attr("href")
|
||||
val driveDoc = app.get(iframeGdbot ?: return null)
|
||||
|
||||
val ssid = driveDoc.cookies["PHPSESSID"]
|
||||
val script = driveDoc.document.selectFirst("script:containsData(var formData)")?.data()
|
||||
|
||||
val baseUrl = getBaseUrl(iframeGdbot)
|
||||
val token = script?.substringAfter("'token', '")?.substringBefore("');")
|
||||
val link =
|
||||
script?.substringAfter("fetch('")?.substringBefore("',").let { "$baseUrl$it" }
|
||||
|
||||
val body = FormBody.Builder()
|
||||
.addEncoded("token", "$token")
|
||||
.build()
|
||||
val cookies = mapOf("PHPSESSID" to "$ssid")
|
||||
|
||||
val result = app.post(
|
||||
link,
|
||||
requestBody = body,
|
||||
headers = mapOf(
|
||||
"Accept" to "*/*",
|
||||
"Origin" to baseUrl,
|
||||
"Sec-Fetch-Site" to "same-origin"
|
||||
),
|
||||
cookies = cookies,
|
||||
referer = iframeGdbot
|
||||
).text
|
||||
return AppUtils.tryParseJson<DriveBotLink>(result)?.url
|
||||
}
|
||||
|
||||
suspend fun bypassFdAds(url: String): String? {
|
||||
val res = app.get(url).document
|
||||
val freeRedirect = res.selectFirst("a#link")?.attr("href")
|
||||
val res2 = app.get(freeRedirect ?: return null , verify = false).document
|
||||
val formLink = res2.select("form#landing").attr("action")
|
||||
val value = res2.select("form#landing input").attr("value")
|
||||
val res3 = app.post(formLink, data = mapOf("go" to value), verify = false).document
|
||||
val formLink2 = res3.select("form#landing").attr("action")
|
||||
val humanVer = res3.select("form#landing input[name=humanverification]").attr("value")
|
||||
val newwp = res3.select("form#landing input[name=newwpsafelink]").attr("value")
|
||||
val res4 = app.post(
|
||||
formLink2,
|
||||
data = mapOf("humanverification" to humanVer, "newwpsafelink" to newwp),
|
||||
verify = false
|
||||
).document
|
||||
val formLink3 = res4.select("form#wpsafelink-landing").attr("action")
|
||||
val newwpsafelink =
|
||||
res4.select("form#wpsafelink-landing input[name=newwpsafelink]").attr("value")
|
||||
val res5 = app.post(
|
||||
formLink3,
|
||||
data = mapOf("newwpsafelink" to newwpsafelink),
|
||||
verify = false
|
||||
).document
|
||||
val finalLink = res5.selectFirst("div#wpsafe-link a")?.attr("onclick")?.substringAfter("open('")
|
||||
?.substringBefore("',")
|
||||
return app.get(finalLink ?: return null, verify = false).url
|
||||
}
|
||||
|
||||
suspend fun getFilmxyCookies(imdbId: String? = null, season: Int? = null): FilmxyCookies? {
|
||||
|
||||
val url = if (season == null) {
|
||||
|
|
Loading…
Reference in a new issue