[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
version = 45
version = 46
cloudstream {

View file

@ -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()
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 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,
"GMovies [$size]",
"GMovies [$size]",
videoLink ?: return@apmap null,
"",
getGMoviesQuality(title)
)
)
}
}
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 request = app.get(url)
if(!request.isSuccessful) return
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)
)
)
}
}
@ -1549,3 +1567,7 @@ 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,
)

View file

@ -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(),
)

View file

@ -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,9 +64,11 @@ 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=")
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=")
val session =
cookiesSet.find { it.second.contains("gdtot_proxy_session") }?.second?.substringAfter("gdtot_proxy_session=")
?.substringBefore(";")
val cookies = mapOf(
@ -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) {