From 5eafdd6e4b2762a91dfd12345b253ae9d9a926e0 Mon Sep 17 00:00:00 2001 From: hexated Date: Wed, 15 Feb 2023 01:01:07 +0700 Subject: [PATCH] sora-lite: added Gomovies --- SoraStream/build.gradle.kts | 2 +- .../main/kotlin/com/hexated/SoraExtractor.kt | 90 +++++++++++++++++++ .../src/main/kotlin/com/hexated/SoraStream.kt | 3 +- .../main/kotlin/com/hexated/SoraStreamLite.kt | 4 + .../src/main/kotlin/com/hexated/SoraUtils.kt | 33 ++++++- 5 files changed, 129 insertions(+), 3 deletions(-) diff --git a/SoraStream/build.gradle.kts b/SoraStream/build.gradle.kts index 70a8cb97..741144e0 100644 --- a/SoraStream/build.gradle.kts +++ b/SoraStream/build.gradle.kts @@ -1,5 +1,5 @@ // use an integer for version numbers -version = 89 +version = 90 cloudstream { diff --git a/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt b/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt index 725f9b4f..a272ebc9 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt @@ -2415,6 +2415,88 @@ object SoraExtractor : SoraStream() { } + suspend fun invokeGomovies( + title: String? = null, + year: Int? = null, + season: Int? = null, + episode: Int? = null, + callback: (ExtractorLink) -> Unit, + ) { + val query = if (season == null) { + title + } else { + "$title Season $season" + } + + val doc = app.get("$gomoviesAPI/search/$query").document + + val media = doc.select("div._gory div.g_yFsxmKnYLvpKDTrdbizeYMWy").map { + Triple( + it.attr("data-filmName"), + it.attr("data-year"), + it.select("a").attr("href") + ) + }.find { + if (season == null) { + (it.first.equals(title, true) || it.first.equals( + "$title ($year)", + true + )) && it.second.equals("$year") + } else { + it.first.equals("$title - Season $season", true) && it.second.equals("$year") + } + } ?: return + + val iframe = if (season == null) { + media.third + } else { + app.get( + fixUrl( + media.third, + gomoviesAPI + ) + ).document.selectFirst("div#g_MXOzFGouZrOAUioXjpddqkZK a:nth-child($episode)") + ?.attr("href") + } ?: return + + val res = app.get(fixUrl(iframe, gomoviesAPI), verify = false) + val match = "var url = '(/user/servers/.*?\\?ep=.*?)';".toRegex().find(res.text) + val serverUrl = match?.groupValues?.get(1) ?: return + val cookies = res.okhttpResponse.headers.getPutlockerCookies() + val url = res.document.select("meta[property=og:url]").attr("content") + val headers = mapOf("X-Requested-With" to "XMLHttpRequest") + val qualities = intArrayOf(2160, 1440, 1080, 720, 480, 360) + app.get( + "$gomoviesAPI$serverUrl", + cookies = cookies, referer = url, headers = headers + ).document.select("ul li").amap { el -> + val server = el.attr("data-value") + val encryptedData = app.get( + "$url?server=$server&_=${System.currentTimeMillis()}", + cookies = cookies, + referer = url, + headers = headers + ).text + val json = base64Decode(encryptedData).putlockerDecrypt() + val links = tryParseJson>(json) ?: return@amap + links.forEach { video -> + qualities.filter { it <= video.max.toInt() }.forEach { + callback( + ExtractorLink( + "Gomovies", + "Gomovies", + video.src.split("360", limit = 3).joinToString(it.toString()), + "$gomoviesAPI/", + it + ) + ) + } + } + } + + } + + } class StreamM4u : XStreamCdn() { @@ -2453,6 +2535,14 @@ data class Movie123Search( @JsonProperty("data") val data: ArrayList? = arrayListOf(), ) +data class GomoviesSources( + @JsonProperty("src") val src: String, + @JsonProperty("file") val file: String? = null, + @JsonProperty("label") val label: Int? = null, + @JsonProperty("max") val max: String, + @JsonProperty("size") val size: String, +) + data class UHDBackupUrl( @JsonProperty("url") val url: String? = null, ) diff --git a/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt b/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt index bca3feb7..5d1d0ce9 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt @@ -110,6 +110,7 @@ open class SoraStream : TmdbProvider() { const val movie123NetAPI = "https://ww7.0123movie.net" const val smashyStreamAPI = "https://embed.smashystream.com" const val watchSomuchAPI = "https://watchsomuch.tv" // sub only + const val gomoviesAPI = "https://gomovies-online.com" const val baymoviesAPI = "https://opengatewayindex.pages.dev" // dead const val chillmovies0API = "https://chill.aicirou.workers.dev/0:" // dead const val chillmovies1API = "https://chill.aicirou.workers.dev/1:" // dead @@ -709,7 +710,7 @@ open class SoraStream : TmdbProvider() { res.episode, callback ) - }, + } ) return true diff --git a/SoraStream/src/main/kotlin/com/hexated/SoraStreamLite.kt b/SoraStream/src/main/kotlin/com/hexated/SoraStreamLite.kt index 906aa683..4c78eb31 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraStreamLite.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraStreamLite.kt @@ -8,6 +8,7 @@ import com.hexated.SoraExtractor.invokeFilmxy import com.hexated.SoraExtractor.invokeFlixhq import com.hexated.SoraExtractor.invokeFlixon import com.hexated.SoraExtractor.invokeFwatayako +import com.hexated.SoraExtractor.invokeGomovies import com.hexated.SoraExtractor.invokeHDMovieBox import com.hexated.SoraExtractor.invokeIdlix import com.hexated.SoraExtractor.invokeKimcartoon @@ -207,6 +208,9 @@ class SoraStreamLite : SoraStream() { { invokeFlixon(res.id, res.imdbId, res.season, res.episode, callback) }, + { + invokeGomovies(res.title, res.year, res.season, res.episode, callback) + }, ) return true diff --git a/SoraStream/src/main/kotlin/com/hexated/SoraUtils.kt b/SoraStream/src/main/kotlin/com/hexated/SoraUtils.kt index 3bdc1b9e..42f29657 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraUtils.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraUtils.kt @@ -16,6 +16,7 @@ import com.lagradost.nicehttp.RequestBodyTypes import com.lagradost.nicehttp.requestCreator import kotlinx.coroutines.delay import okhttp3.FormBody +import okhttp3.Headers import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.RequestBody.Companion.toRequestBody @@ -644,12 +645,42 @@ var arrayofworkers = (.*)""".toRegex() return BaymoviesConfig(country, downloadTime, workers) } -// taken from https://github.com/821938089/cloudstream-extensions/blob/6e41697cbf816d2f57d9922d813c538e3192f708/PiousIndexProvider/src/main/kotlin/com/horis/cloudstreamplugins/PiousIndexProvider.kt#L175-L179 +/** taken from https://github.com/821938089/cloudstream-extensions/blob/6e41697cbf816d2f57d9922d813c538e3192f708/PiousIndexProvider/src/main/kotlin/com/horis/cloudstreamplugins/PiousIndexProvider.kt#L175-L179 +- Credits to Horis +**/ fun decodeIndexJson(json: String): String { val slug = json.reversed().substring(24) return base64Decode(slug.substring(0, slug.length - 20)) } +/** taken from https://github.com/821938089/cloudstream-extensions/blob/23dae833a48fb329d4c67dd77ac1e8bb592ac5a9/Movie123Provider/src/main/kotlin/com/horis/cloudstreamplugins/Movie123Provider.kt#L138-L150 +- Credits to Horis +**/ +fun String.putlockerDecrypt(key: String = "123"): String { + val sb = StringBuilder() + var i = 0 + while (i < this.length) { + var j = 0 + while (j < key.length && i < this.length) { + sb.append((this[i].code xor key[j].code).toChar()) + j++ + i++ + } + } + return sb.toString() +} + +fun Headers.getPutlockerCookies(cookieKey: String = "set-cookie"): Map { + val cookieList = + this.filter { it.first.equals(cookieKey, ignoreCase = true) }.mapNotNull { + it.second.split(";").firstOrNull() + } + return cookieList.associate { + val split = it.split("=", limit = 2) + (split.getOrNull(0)?.trim() ?: "") to (split.getOrNull(1)?.trim() ?: "") + }.filter { it.key.isNotBlank() && it.value.isNotBlank() } +} + fun String?.createSlug(): String? { return this?.replace(Regex("[!%:'?,]|( &)"), "")?.replace(" ", "-")?.lowercase() ?.replace("-–-", "-")