[Sora:Test] Adding FDMovies

This commit is contained in:
hexated 2022-12-07 22:06:48 +07:00
parent 89116dc209
commit df4178f088
4 changed files with 144 additions and 107 deletions

View file

@ -1,5 +1,5 @@
// use an integer for version numbers // use an integer for version numbers
version = 45 version = 46
cloudstream { cloudstream {

View file

@ -1,5 +1,6 @@
package com.hexated package com.hexated
import android.util.Log
import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.utils.* import com.lagradost.cloudstream3.utils.*
@ -1354,47 +1355,64 @@ object SoraExtractor : SoraStream() {
})?.filter { it.first.contains("gdtot") } ?: return })?.filter { it.first.contains("gdtot") } ?: return
iframe.apmap { (iframeLink, title) -> iframe.apmap { (iframeLink, title) ->
val size = Regex("(?i)\\s(\\S+gb|mb)").find(title)?.groupValues?.getOrNull(1)
val gdBotLink = extractGdbot(iframeLink) val gdBotLink = extractGdbot(iframeLink)
val iframeGdbot = app.get( val videoLink = extractDrivebot(gdBotLink ?: return@apmap null)
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 ssid = driveDoc.cookies["PHPSESSID"] callback.invoke(
val script = driveDoc.document.selectFirst("script:containsData(var formData)")?.data() ExtractorLink(
"GMovies [$size]",
"GMovies [$size]",
videoLink ?: return@apmap null,
"",
getGMoviesQuality(title)
)
)
}
}
val baseUrl = getBaseUrl(iframeGdbot) suspend fun invokeFDMovies(
val token = script?.substringAfter("'token', '")?.substringBefore("');") title: String? = null,
val link = script?.substringAfter("fetch('")?.substringBefore("',").let { "$baseUrl$it" } 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() val request = app.get(url)
.addEncoded("token", "$token") if(!request.isSuccessful) return
.build()
val cookies = mapOf("PHPSESSID" to "$ssid")
val size = Regex("(?i)\\s(\\S+gb|mb)").find(title)?.groupValues?.getOrNull(1)?.let { "[$it]" } ?: "" val iframe = request.document.select("div#download tbody tr").map { it }
app.post( .filter { it.select("img").attr("src").contains("gdtot") }.map {
link, Triple(
requestBody = body, it.select("a").attr("href"),
headers = mapOf( it.select("strong.quality").text(),
"Accept" to "*/*", it.select("td:nth-child(4)").text()
"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)
)
) )
} }
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)
)
)
} }
} }
@ -1549,3 +1567,7 @@ data class SourcesFwatayako(
@JsonProperty("movie") val sourcesMovie: String? = null, @JsonProperty("movie") val sourcesMovie: String? = null,
@JsonProperty("tv") val sourcesTv: ArrayList<SeasonFwatayako>? = arrayListOf(), @JsonProperty("tv") val sourcesTv: ArrayList<SeasonFwatayako>? = arrayListOf(),
) )
data class DriveBotLink(
@JsonProperty("url") val url: String? = null,
)

View file

@ -11,7 +11,6 @@ import com.hexated.SoraExtractor.invokeIdlix
import com.hexated.SoraExtractor.invokeKimcartoon import com.hexated.SoraExtractor.invokeKimcartoon
import com.hexated.SoraExtractor.invokeMovieHab import com.hexated.SoraExtractor.invokeMovieHab
import com.hexated.SoraExtractor.invokeNoverse import com.hexated.SoraExtractor.invokeNoverse
import com.hexated.SoraExtractor.invokeOlgply
import com.hexated.SoraExtractor.invokeSeries9 import com.hexated.SoraExtractor.invokeSeries9
import com.hexated.SoraExtractor.invokeSoraVIP import com.hexated.SoraExtractor.invokeSoraVIP
import com.hexated.SoraExtractor.invokeTwoEmbed import com.hexated.SoraExtractor.invokeTwoEmbed
@ -22,6 +21,7 @@ import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
import com.lagradost.cloudstream3.metaproviders.TmdbProvider import com.lagradost.cloudstream3.metaproviders.TmdbProvider
import com.hexated.SoraExtractor.invoZoro import com.hexated.SoraExtractor.invoZoro
import com.hexated.SoraExtractor.invokeFDMovies
import com.hexated.SoraExtractor.invokeFwatayako import com.hexated.SoraExtractor.invokeFwatayako
import com.hexated.SoraExtractor.invokeGMovies import com.hexated.SoraExtractor.invokeGMovies
import com.hexated.SoraExtractor.invokeLing import com.hexated.SoraExtractor.invokeLing
@ -52,7 +52,7 @@ open class SoraStream : TmdbProvider() {
const val gdbot = "https://gdbot.xyz" const val gdbot = "https://gdbot.xyz"
private val mainAPI = base64DecodeAPI("cHA=LmE=ZWw=cmM=dmU=aC4=dGM=d2E=eHA=Ly8=czo=dHA=aHQ=") 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 twoEmbedAPI = "https://www.2embed.to"
const val vidSrcAPI = "https://v2.vidsrc.me" const val vidSrcAPI = "https://v2.vidsrc.me"
const val dbgoAPI = "https://dbgo.fun" const val dbgoAPI = "https://dbgo.fun"
@ -75,6 +75,7 @@ open class SoraStream : TmdbProvider() {
const val uhdmoviesAPI = "https://uhdmovies.site" const val uhdmoviesAPI = "https://uhdmovies.site"
const val fwatayakoAPI = "https://5100.svetacdn.in" const val fwatayakoAPI = "https://5100.svetacdn.in"
const val gMoviesAPI = "https://gdrivemovies.xyz" const val gMoviesAPI = "https://gdrivemovies.xyz"
const val fdMoviesAPI = "https://freedrivemovie.com"
fun getType(t: String?): TvType { fun getType(t: String?): TvType {
return when (t) { return when (t) {
@ -166,12 +167,13 @@ open class SoraStream : TmdbProvider() {
override suspend fun load(url: String): LoadResponse? { override suspend fun load(url: String): LoadResponse? {
val data = parseJson<Data>(url) val data = parseJson<Data>(url)
val type = getType(data.type) 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" "$tmdbAPI/movie/${data.id}?api_key=$apiKey&append_to_response=credits,external_ids,videos,recommendations"
} else { } else {
"$tmdbAPI/tv/${data.id}?api_key=$apiKey&append_to_response=credits,external_ids,videos,recommendations" "$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 title = res.title ?: res.name ?: return null
val poster = getOriImageUrl(res.posterPath) val poster = getOriImageUrl(res.posterPath)
@ -180,7 +182,7 @@ open class SoraStream : TmdbProvider() {
val year = (res.releaseDate ?: res.firstAirDate)?.split("-")?.first()?.toIntOrNull() val year = (res.releaseDate ?: res.firstAirDate)?.split("-")?.first()?.toIntOrNull()
val rating = res.vote_average.toString().toRatingInt() val rating = res.vote_average.toString().toRatingInt()
val genres = res.genres?.mapNotNull { it.name } 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 -> val actors = res.credits?.cast?.mapNotNull { cast ->
ActorData( ActorData(
@ -191,7 +193,8 @@ open class SoraStream : TmdbProvider() {
roleString = cast.character roleString = cast.character
) )
} ?: return null } ?: 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}" } val trailer = res.videos?.results?.map { "https://www.youtube.com/watch?v=${it.key}" }
?.randomOrNull() ?.randomOrNull()
@ -212,7 +215,7 @@ open class SoraStream : TmdbProvider() {
title = title, title = title,
year = season.airDate?.split("-")?.first()?.toIntOrNull(), year = season.airDate?.split("-")?.first()?.toIntOrNull(),
orgTitle = orgTitle, orgTitle = orgTitle,
show = show, isAnime = isAnime,
airedYear = year, airedYear = year,
lastSeason = lastSeason lastSeason = lastSeason
).toJson(), ).toJson(),
@ -256,7 +259,7 @@ open class SoraStream : TmdbProvider() {
title = title, title = title,
year = year, year = year,
orgTitle = orgTitle, orgTitle = orgTitle,
show = show, isAnime = isAnime,
).toJson(), ).toJson(),
) { ) {
this.posterUrl = poster this.posterUrl = poster
@ -280,16 +283,6 @@ open class SoraStream : TmdbProvider() {
): Boolean { ): Boolean {
val res = parseJson<LinkData>(data) 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( argamap(
{ {
@ -302,35 +295,6 @@ open class SoraStream : TmdbProvider() {
callback 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) 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.id,
res.season, res.season,
res.episode, 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) 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.title,
res.year, res.year,
res.season, res.season,
@ -457,7 +421,10 @@ open class SoraStream : TmdbProvider() {
invokeFwatayako(res.imdbId, res.season, res.episode, subtitleCallback, callback) 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 title: String? = null,
val year: Int? = null, val year: Int? = null,
val orgTitle: String? = null, val orgTitle: String? = null,
val show: String? = null, val isAnime: Boolean = false,
val airedYear: Int? = null, val airedYear: Int? = null,
val lastSeason: Int? = null, val lastSeason: Int? = null,
) )
@ -487,23 +454,6 @@ open class SoraStream : TmdbProvider() {
val malId: Int? = null, 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( data class Results(
@JsonProperty("results") val results: ArrayList<Media>? = arrayListOf(), @JsonProperty("results") val results: ArrayList<Media>? = arrayListOf(),
) )

View file

@ -5,10 +5,12 @@ import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.base64Decode import com.lagradost.cloudstream3.base64Decode
import com.lagradost.cloudstream3.base64Encode import com.lagradost.cloudstream3.base64Encode
import com.lagradost.cloudstream3.network.WebViewResolver import com.lagradost.cloudstream3.network.WebViewResolver
import com.lagradost.cloudstream3.utils.AppUtils
import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.Qualities import com.lagradost.cloudstream3.utils.Qualities
import com.lagradost.cloudstream3.utils.getQualityFromName import com.lagradost.cloudstream3.utils.getQualityFromName
import com.lagradost.nicehttp.requestCreator import com.lagradost.nicehttp.requestCreator
import okhttp3.FormBody
import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.HttpUrl.Companion.toHttpUrl
import java.net.URI 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 token = res.document.selectFirst("input[name=_token]")?.attr("value")
val cookiesSet = res.headers.filter { it.first == "set-cookie" } val cookiesSet = res.headers.filter { it.first == "set-cookie" }
val xsrf = cookiesSet.find { it.second.contains("XSRF-TOKEN") }?.second?.substringAfter("XSRF-TOKEN=") val xsrf =
?.substringBefore(";") cookiesSet.find { it.second.contains("XSRF-TOKEN") }?.second?.substringAfter("XSRF-TOKEN=")
val session = cookiesSet.find { it.second.contains("gdtot_proxy_session") }?.second?.substringAfter("gdtot_proxy_session=") ?.substringBefore(";")
?.substringBefore(";") val session =
cookiesSet.find { it.second.contains("gdtot_proxy_session") }?.second?.substringAfter("gdtot_proxy_session=")
?.substringBefore(";")
val cookies = mapOf( val cookies = mapOf(
"gdtot_proxy_session" to "$session", "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") 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? { suspend fun getFilmxyCookies(imdbId: String? = null, season: Int? = null): FilmxyCookies? {
val url = if (season == null) { val url = if (season == null) {