From 5ebced9262503bb3992eaf887ea702c0ba6bd9ce Mon Sep 17 00:00:00 2001 From: hexated Date: Wed, 2 Nov 2022 13:56:24 +0700 Subject: [PATCH] update Sora Home and add new Source --- SoraStream/build.gradle.kts | 2 +- .../main/kotlin/com/hexated/SoraExtractor.kt | 145 +++++++++++++++++- .../src/main/kotlin/com/hexated/SoraStream.kt | 23 ++- build.gradle.kts | 2 + 4 files changed, 166 insertions(+), 6 deletions(-) diff --git a/SoraStream/build.gradle.kts b/SoraStream/build.gradle.kts index e9a2662c..e6b79b19 100644 --- a/SoraStream/build.gradle.kts +++ b/SoraStream/build.gradle.kts @@ -1,5 +1,5 @@ // use an integer for version numbers -version = 19 +version = 20 cloudstream { diff --git a/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt b/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt index e6f83ed7..2c8112b1 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt @@ -1,13 +1,21 @@ package com.hexated import com.fasterxml.jackson.annotation.JsonProperty +import com.hexated.SoraStream.Companion.filmxyAPI import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.network.WebViewResolver import com.lagradost.cloudstream3.utils.* import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson +import com.lagradost.nicehttp.Requests +import com.lagradost.nicehttp.Session import com.lagradost.nicehttp.requestCreator +import okhttp3.HttpUrl.Companion.toHttpUrl +import com.google.gson.JsonParser +import okhttp3.RequestBody.Companion.toRequestBody import java.net.URI +val session = Session(Requests().baseClient) + object SoraExtractor : SoraStream() { suspend fun invokeLocalSources( @@ -638,11 +646,142 @@ object SoraExtractor : SoraStream() { } + suspend fun invokeFilmxy( + imdbId: String? = null, + season: Int? = null, + episode: Int? = null, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ) { + val url = if (season == null) { + "${filmxyAPI}/movie/$imdbId" + } else { + "${filmxyAPI}/tv/$imdbId" + } + val filmxyCookies = getFilmxyCookies(imdbId, season) + + val cookiesDoc = mapOf( + "G_ENABLED_IDPS" to "google", + "wordpress_logged_in_8bf9d5433ac88cc9a3a396d6b154cd01" to filmxyCookies.wLog, + "PHPSESSID" to filmxyCookies.phpsessid + ) + + val doc = session.get(url, cookies = cookiesDoc).document + val script = doc.selectFirst("script:containsData(var isSingle)")?.data().toString() + val sourcesData = Regex("listSE\\s*=\\s?(.*?),[\\n|\\s]").find(script)?.groupValues?.get(1) + val sourcesDetail = Regex("linkDetails\\s*=\\s?(.*?),[\\n|\\s]").find(script)?.groupValues?.get(1) + + //Gson is shit, but i don't care + val sourcesJson = JsonParser().parse(sourcesData).asJsonObject + val sourcesDetailJson = JsonParser().parse(sourcesDetail).asJsonObject + + val sources = if (season == null && episode == null) { + sourcesJson.getAsJsonObject("movie").getAsJsonArray("movie") + } else { + val eps = if (episode!! < 10) "0$episode" else episode + val sson = if (season!! < 10) "0$season" else season + sourcesJson.getAsJsonObject("s$sson").getAsJsonArray("e$eps") + }.asJsonArray + + val scriptUser = doc.select("script").find { it.data().contains("var userNonce") }?.data().toString() + val userNonce = Regex("var\\suserNonce.*?[\"|'](\\S+?)[\"|'];").find(scriptUser)?.groupValues?.get(1) + val userId = Regex("var\\suser_id.*?[\"|'](\\S+?)[\"|'];").find(scriptUser)?.groupValues?.get(1) + val linkIDs = sources.joinToString("") { + "&linkIDs%5B%5D=$it" + }.replace("\"", "") + + val body = "action=get_vid_links$linkIDs&user_id=$userId&nonce=$userNonce".toRequestBody() + val cookiesJson = mapOf( + "G_ENABLED_IDPS" to "google", + "PHPSESSID" to filmxyCookies.phpsessid, + "wordpress_logged_in_8bf9d5433ac88cc9a3a396d6b154cd01" to filmxyCookies.wLog, + "wordpress_sec_8bf9d5433ac88cc9a3a396d6b154cd01" to filmxyCookies.wSec + ) + val json = app.post( + "$filmxyAPI/wp-admin/admin-ajax.php", + requestBody = body, + referer = url, + headers = mapOf( + "Accept" to "*/*", + "DNT" to "1", + "Content-Type" to "application/x-www-form-urlencoded; charset=UTF-8", + "Origin" to filmxyAPI, + "X-Requested-With" to "XMLHttpRequest", + ), + cookies = cookiesJson + ).text.let { JsonParser().parse(it).asJsonObject } + + sources.map { source -> + val src = source.asString + val link = json.getAsJsonPrimitive(src).asString + val quality = sourcesDetailJson.getAsJsonObject(src).getAsJsonPrimitive("resolution").asString + val server = sourcesDetailJson.getAsJsonObject(src).getAsJsonPrimitive("server").asString + val size = sourcesDetailJson.getAsJsonObject(src).getAsJsonPrimitive("size").asString + + callback.invoke( + ExtractorLink( + "Filmxy $size ($server)", + "Filmxy $size ($server)", + link, + "$filmxyAPI/", + getQualityFromName(quality) + ) + ) + } + + } + } -//private fun fixTitle(title: String? = null) : String? { -// return title?.replace(":", "")?.replace(" ", "-")?.lowercase()?.replace("-–-", "-") -//} +data class FilmxyCookies( + val phpsessid: String, + val wLog: String, + val wSec: String, +) + +suspend fun getFilmxyCookies(imdbId: String? = null, season: Int? = null): FilmxyCookies { + + val url = if (season == null) { + "${filmxyAPI}/movie/$imdbId" + } else { + "${filmxyAPI}/tv/$imdbId" + } + val cookieUrl = "${filmxyAPI}/wp-admin/admin-ajax.php" + + val res = session.get( + url, + headers = mapOf( + "Accept" to "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8" + ), + ) + + val userNonce = + res.document.select("script").find { it.data().contains("var userNonce") }?.data()?.let { + Regex("var\\suserNonce.*?\"(\\S+?)\";").find(it)?.groupValues?.get(1) + } + + var phpsessid = session.baseClient.cookieJar.loadForRequest(url.toHttpUrl()) + .first { it.name == "PHPSESSID" }.value + + session.post( + cookieUrl, + data = mapOf( + "action" to "guest_login", + "nonce" to "$userNonce", + ), + headers = mapOf( + "Cookie" to "PHPSESSID=$phpsessid; G_ENABLED_IDPS=google", + "X-Requested-With" to "XMLHttpRequest", + ) + ) + + val cookieJar = session.baseClient.cookieJar.loadForRequest(cookieUrl.toHttpUrl()) + phpsessid = cookieJar.first { it.name == "PHPSESSID" }.value + val wLog = cookieJar.first { it.name == "wordpress_logged_in_8bf9d5433ac88cc9a3a396d6b154cd01" }.value + val wSec = cookieJar.first { it.name == "wordpress_sec_8bf9d5433ac88cc9a3a396d6b154cd01" }.value + + return FilmxyCookies(phpsessid, wLog, wSec) +} private fun String?.fixTitle(): String? { return this?.replace(":", "")?.replace(" ", "-")?.lowercase()?.replace("-–-", "-") diff --git a/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt b/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt index 89cf8f60..ab28eeaf 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt @@ -5,6 +5,7 @@ import com.hexated.RandomUserAgent.getRandomUserAgent import com.hexated.SoraExtractor.invoke123Movie import com.hexated.SoraExtractor.invokeDatabaseGdrive import com.hexated.SoraExtractor.invokeDbgo +import com.hexated.SoraExtractor.invokeFilmxy import com.hexated.SoraExtractor.invokeGogo import com.hexated.SoraExtractor.invokeHDMovieBox import com.hexated.SoraExtractor.invokeIdlix @@ -45,8 +46,7 @@ open class SoraStream : TmdbProvider() { private const val tmdbAPI = "https://api.themoviedb.org/3" private const val apiKey = "b030404650f279792a8d3287232358e3" // PLEASE DON'T STEAL val mainAPI = base64DecodeAPI("cHA=LmE=ZWw=cmM=dmU=aC4=dGM=d2E=eHA=Ly8=czo=dHA=aHQ=") - val mainServerAPI = - base64DecodeAPI("cA==YXA=bC4=Y2U=ZXI=LnY=aWU=b3Y=LW0=cmE=c28=Ly8=czo=dHA=aHQ=") + 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" @@ -59,6 +59,7 @@ open class SoraStream : TmdbProvider() { const val noverseAPI = "https://www.nollyverse.com" const val olgplyAPI = "https://olgply.xyz" const val uniqueStreamAPI = "https://uniquestream.net" + const val filmxyAPI = "https://www.filmxy.vip" fun getType(t: String?): TvType { return when (t) { @@ -87,11 +88,25 @@ open class SoraStream : TmdbProvider() { } } + private suspend fun checkMainServer() { + val check = app.get(mainServerAPI) + mainServerAPI = if(check.isSuccessful) { + mainServerAPI + } else { + base64DecodeAPI("cHA=LmE=ZWw=cmM=dmU=Ny4=bjQ=cmE=aHQ=YW4=a2g=ZS0=dmk=bW8=eC0=bWk=cmU=Ly8=czo=dHA=aHQ=") + } + } + override val mainPage = mainPageOf( "$tmdbAPI/tv/airing_today?api_key=$apiKey®ion=&page=" to "Airing Today TV Shows", "$tmdbAPI/movie/popular?api_key=$apiKey®ion=&page=" to "Popular Movies", "$tmdbAPI/tv/popular?api_key=$apiKey®ion=&page=" to "Popular TV Shows", "$tmdbAPI/tv/on_the_air?api_key=$apiKey®ion=&page=" to "On The Air TV Shows", + "$tmdbAPI/discover/tv?api_key=$apiKey&with_networks=213&page=" to "Netflix", + "$tmdbAPI/discover/tv?api_key=$apiKey&with_networks=1024&page=" to "Amazon", + "$tmdbAPI/discover/tv?api_key=$apiKey&with_networks=2739&page=" to "Disney+", + "$tmdbAPI/discover/tv?api_key=$apiKey&with_networks=453&page=" to "Hulu", + "$tmdbAPI/discover/tv?api_key=$apiKey&with_networks=2552&page=" to "Apple TV+", "$tmdbAPI/movie/top_rated?api_key=$apiKey®ion=&page=" to "Top Rated Movies", "$tmdbAPI/tv/top_rated?api_key=$apiKey®ion=&page=" to "Top Rated TV Shows", "$tmdbAPI/movie/upcoming?api_key=$apiKey®ion=&page=" to "Upcoming Movies", @@ -108,6 +123,7 @@ open class SoraStream : TmdbProvider() { page: Int, request: MainPageRequest ): HomePageResponse { + checkMainServer() val type = if (request.data.contains("/movie")) "movie" else "tv" val home = app.get(request.data + page) .parsedSafe()?.results @@ -392,6 +408,9 @@ open class SoraStream : TmdbProvider() { }, { invokeUniqueStream(res.title, res.year, res.season, res.episode, subtitleCallback, callback) + }, + { + invokeFilmxy(res.imdbId, res.season, res.episode, subtitleCallback, callback) } ) diff --git a/build.gradle.kts b/build.gradle.kts index 8bc2c884..48d2cbf0 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -84,6 +84,8 @@ subprojects { implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4") // html parser //run JS implementation("org.mozilla:rhino:1.7.14") + implementation("com.google.code.gson:gson:2.8.9") + } }