diff --git a/LayarKacaProvider/src/main/kotlin/com/hexated/LayarKacaProvider.kt b/LayarKacaProvider/src/main/kotlin/com/hexated/LayarKacaProvider.kt index cbccb55d..0fb6df58 100644 --- a/LayarKacaProvider/src/main/kotlin/com/hexated/LayarKacaProvider.kt +++ b/LayarKacaProvider/src/main/kotlin/com/hexated/LayarKacaProvider.kt @@ -6,10 +6,9 @@ import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer import com.lagradost.cloudstream3.extractors.Filesim import com.lagradost.cloudstream3.utils.* import org.jsoup.nodes.Element -import java.net.URLDecoder class LayarKacaProvider : MainAPI() { - override var mainUrl = "https://tv3.lk21official.pro" + override var mainUrl = "https://tv.lk21official.wiki" private var seriesUrl = "https://tv1.nontondrama.click" override var name = "LayarKaca" override val hasMainPage = true diff --git a/SoraStream/build.gradle.kts b/SoraStream/build.gradle.kts index 35b0760a..abd1ba5d 100644 --- a/SoraStream/build.gradle.kts +++ b/SoraStream/build.gradle.kts @@ -1,7 +1,7 @@ import org.jetbrains.kotlin.konan.properties.Properties // use an integer for version numbers -version = 169 +version = 170 android { defaultConfig { diff --git a/SoraStream/src/main/kotlin/com/hexated/Extractors.kt b/SoraStream/src/main/kotlin/com/hexated/Extractors.kt index af9fdff7..065b7c1b 100644 --- a/SoraStream/src/main/kotlin/com/hexated/Extractors.kt +++ b/SoraStream/src/main/kotlin/com/hexated/Extractors.kt @@ -186,11 +186,6 @@ open class VCloud : ExtractorApi() { } -class HubcloudLol : VCloud() { - override val name = "Hubcloud" - override val mainUrl = "https://hubcloud.lol" -} - class Hubcloud : VCloud() { override val name = "Hubcloud" override val mainUrl = "https://hubcloud.in" diff --git a/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt b/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt index 53f50a01..a961abae 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt @@ -580,6 +580,56 @@ object SoraExtractor : SoraStream() { } + suspend fun invokeWatchflx( + tmdbId: Int? = null, + season: Int? = null, + episode: Int? = null, + callback: (ExtractorLink) -> Unit + ) { + val epsSlug = getEpisodeSlug(season, episode) + val cookies = getWatchflxCookies() + val url = if (season == null) { + "$watchflxAPI/browse/playmovie/$tmdbId/directplay" + } else { + "$watchflxAPI/Playseries/series/$tmdbId/directplay" + } + val res = app.get(url, cookies = cookies).document + + val showUrl = if (season == null) { + res.selectFirst("iframe.movie_player")?.attr("src") + } else { + val seasonUrl = + res.select("ul.nav.nav-tabs.tabs-left li:matches(Season $season\$) a").attr("href") + val episodeUrl = app.get( + seasonUrl, + cookies = cookies + ).document.select("div.thumb-nail-list a:contains(${epsSlug.second}:)").attr("href") + app.get(episodeUrl, cookies = cookies).document.selectFirst("iframe.movie_player") + ?.attr("src") + } + val iframe = app.get( + showUrl ?: return, referer = "$watchflxAPI/" + ).document.selectFirst("div#the_frame iframe")?.attr("src") + ?.let { fixUrl(it, getBaseUrl(showUrl)) } ?: return + + val video = app.get(iframe.replace("/loc/", "/pro/"), referer = iframe).text.let { + """mp4_url\s*=\s*["'](.*)["'];""".toRegex().find(it)?.groupValues?.getOrNull(1) + } + + callback.invoke( + ExtractorLink( + "Watchflx", + "Watchflx", + video ?: return, + "$watchflxAPI/", + Qualities.P1080.value, + INFER_TYPE + ) + ) + + + } + suspend fun invokeKimcartoon( title: String? = null, season: Int? = null, @@ -1144,7 +1194,13 @@ object SoraExtractor : SoraStream() { val aTag = if (season == null) "Download Now" else "V-Cloud" res.select("div.entry-content > $hTag:matches(1080p|2160p)").apmap { val tags = """(?:1080p|2160p)(.*)""".toRegex().find(it.text())?.groupValues?.get(1)?.trim() - val href = it.nextElementSibling()?.select("a:contains($aTag)")?.attr("href") + val href = it.nextElementSibling()?.select("a:contains($aTag)")?.attr("href")?.let { url -> + app.post( + "${getBaseUrl(url)}/red.php", + data = mapOf("link" to url), + referer = "$vegaMoviesAPI/" + ).text.substringAfter("location.href = \"").substringBefore("\"") + } val selector = if (season == null) "p a:contains(V-Cloud)" else "h4:matches(0?$episode) + p a:contains(V-Cloud)" val server = app.get(href ?: return@apmap).document.selectFirst("div.entry-content > $selector") ?.attr("href") @@ -1233,6 +1289,45 @@ object SoraExtractor : SoraStream() { } + suspend fun invokeHdmovies4u( + title: String? = null, + imdbId: String? = null, + season: Int? = null, + episode: Int? = null, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ) { + fun String.decodeLink(): String { + return base64Decode(this.substringAfterLast("/")) + } + val (seasonSlug, episodeSlug) = getEpisodeSlug(season, episode) + val media = app.get("$hdmovies4uAPI/?s=${if (season == null) imdbId else title}").document + .let { + val selector = if (season == null) "a" else "a:matches((?i)$title.*Season $season)" + it.selectFirst("div.gridxw.gridxe $selector")?.attr("href") + } + val selector = if (season == null) "1080p|2160p" else "(?i)Episode.*(?:1080p|2160p)" + app.get( + media ?: return + ).document.select("section h4:matches($selector)").apmap { ele -> + val (tags, size) = ele.select("span").map { + it.text() + }.let { it[it.lastIndex - 1] to it.last().substringAfter("-").trim() } + val link = ele.nextElementSibling()?.select("a:contains(DriveTOT)")?.attr("href") + val iframe = bypassBqrecipes(link?.decodeLink() ?: return@apmap).let { + if (it?.contains("/pack/") == true) { + val href = + app.get(it).document.select("table tbody tr:contains(S${seasonSlug}E${episodeSlug}) a") + .attr("href") + bypassBqrecipes(href.decodeLink()) + } else { + it + } + } + invokeDrivetot(iframe ?: return@apmap, tags, size, subtitleCallback, callback) + } + } + suspend fun invokeFwatayako( imdbId: String? = null, season: Int? = null, @@ -1859,25 +1954,12 @@ object SoraExtractor : SoraStream() { invokeSmashyFfix(it.second, it.first, url, callback) } - it.first.contains("/gtop") -> { - invokeSmashyGtop(it.second, it.first, callback) - } - - it.first.contains("/dude_tv") -> { - invokeSmashyDude(it.second, it.first, callback) - } - - it.first.contains("/rip") -> { - invokeSmashyRip(it.second, it.first, subtitleCallback, callback) - } - - it.first.contains("/im.php") && !isAnime -> { - invokeSmashyIm(it.second, it.first, subtitleCallback, callback) - } - - it.first.contains("/rw.php") && !isAnime -> { - invokeSmashyRw(it.second, it.first, subtitleCallback, callback) - } + it.second.equals("Player FM", true) && !isAnime -> invokeSmashyFm( + it.second, + it.first, + url, + callback + ) else -> return@apmap } diff --git a/SoraStream/src/main/kotlin/com/hexated/SoraParser.kt b/SoraStream/src/main/kotlin/com/hexated/SoraParser.kt index e97f9ccb..c28337f0 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraParser.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraParser.kt @@ -141,16 +141,6 @@ data class FDAds( @JsonProperty("linkr") val linkr: String? = null, ) -data class Smashy1Tracks( - @JsonProperty("file") val file: String? = null, - @JsonProperty("label") val label: String? = null, -) - -data class Smashy1Source( - @JsonProperty("file") val file: String? = null, - @JsonProperty("tracks") val tracks: ArrayList? = arrayListOf(), -) - data class WatchsomuchTorrents( @JsonProperty("id") val id: Int? = null, @JsonProperty("movieId") val movieId: Int? = null, @@ -240,11 +230,6 @@ data class CryMoviesResponse( @JsonProperty("streams") val streams: List? = null, ) -data class DudetvSources( - @JsonProperty("file") val file: String? = null, - @JsonProperty("title") val title: String? = null, -) - data class FmoviesResponses( @JsonProperty("result") val result: FmoviesResult? = null, ) diff --git a/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt b/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt index 6ec44cd5..dd0a98c9 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt @@ -38,6 +38,7 @@ import com.hexated.SoraExtractor.invokeSmashyStream import com.hexated.SoraExtractor.invokeDumpStream import com.hexated.SoraExtractor.invokeEmovies import com.hexated.SoraExtractor.invokeFourCartoon +import com.hexated.SoraExtractor.invokeHdmovies4u import com.hexated.SoraExtractor.invokeJump1 import com.hexated.SoraExtractor.invokeMoment import com.hexated.SoraExtractor.invokeMultimovies @@ -50,6 +51,7 @@ import com.hexated.SoraExtractor.invokeUhdmovies import com.hexated.SoraExtractor.invokeVegamovies import com.hexated.SoraExtractor.invokeVidsrcto import com.hexated.SoraExtractor.invokeWatchOnline +import com.hexated.SoraExtractor.invokeWatchflx import com.hexated.SoraExtractor.invokeWatchsomuch import com.lagradost.cloudstream3.LoadResponse.Companion.addImdbId import com.lagradost.cloudstream3.LoadResponse.Companion.addTMDbId @@ -134,6 +136,8 @@ open class SoraStream : TmdbProvider() { const val jump1API = "https://ca.jump1.net" const val vegaMoviesAPI = "https://vegamovies.im" const val netflixAPI = "https://m.netflixmirror.com" + const val hdmovies4uAPI = "https://hdmovies4u.name" + const val watchflxAPI = "https://watchflx.tv" // INDEX SITE const val dahmerMoviesAPI = "https://edytjedhgmdhm.abfhaqrhbnf.workers.dev" @@ -748,6 +752,19 @@ open class SoraStream : TmdbProvider() { res.episode, callback ) + }, + { + if (!res.isAnime) invokeHdmovies4u( + res.title, + res.imdbId, + res.season, + res.episode, + subtitleCallback, + callback + ) + }, + { + if (!res.isAnime) invokeWatchflx(res.id, res.season, res.episode, callback) } ) diff --git a/SoraStream/src/main/kotlin/com/hexated/SoraStreamLite.kt b/SoraStream/src/main/kotlin/com/hexated/SoraStreamLite.kt index ada25e6a..67a22c68 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraStreamLite.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraStreamLite.kt @@ -34,6 +34,7 @@ import com.hexated.SoraExtractor.invokePrimewire import com.hexated.SoraExtractor.invokeVidSrc import com.hexated.SoraExtractor.invokeVidsrcto import com.hexated.SoraExtractor.invokeWatchOnline +import com.hexated.SoraExtractor.invokeWatchflx import com.hexated.SoraExtractor.invokeWatchsomuch import com.lagradost.cloudstream3.SubtitleFile import com.lagradost.cloudstream3.argamap @@ -320,6 +321,14 @@ class SoraStreamLite : SoraStream() { callback ) }, + { + if (!res.isAnime) invokeWatchflx( + res.id, + res.season, + res.episode, + callback + ) + }, { if(!res.isAnime) invoke2embed( res.imdbId, diff --git a/SoraStream/src/main/kotlin/com/hexated/SoraStreamPlugin.kt b/SoraStream/src/main/kotlin/com/hexated/SoraStreamPlugin.kt index 22d8b615..758ba3f5 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraStreamPlugin.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraStreamPlugin.kt @@ -21,6 +21,5 @@ class SoraStreamPlugin: Plugin() { registerExtractorAPI(VCloud()) registerExtractorAPI(Pixeldra()) registerExtractorAPI(Hubcloud()) - registerExtractorAPI(HubcloudLol()) } } \ No newline at end of file diff --git a/SoraStream/src/main/kotlin/com/hexated/SoraUtils.kt b/SoraStream/src/main/kotlin/com/hexated/SoraUtils.kt index b1187efa..3e6ca010 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraUtils.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraUtils.kt @@ -9,10 +9,12 @@ import com.hexated.SoraStream.Companion.crunchyrollAPI import com.hexated.SoraStream.Companion.filmxyAPI import com.hexated.SoraStream.Companion.fmoviesAPI import com.hexated.SoraStream.Companion.gdbot +import com.hexated.SoraStream.Companion.hdmovies4uAPI import com.hexated.SoraStream.Companion.malsyncAPI import com.hexated.SoraStream.Companion.smashyStreamAPI import com.hexated.SoraStream.Companion.tvMoviesAPI import com.hexated.SoraStream.Companion.watchOnlineAPI +import com.hexated.SoraStream.Companion.watchflxAPI import com.hexated.SoraStream.Companion.watchhubApi import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.APIHolder.getCaptchaToken @@ -43,6 +45,8 @@ import javax.crypto.spec.SecretKeySpec import kotlin.collections.ArrayList import kotlin.math.min +var watchflxCookies: Map? = null +var filmxyCookies: Map? = null val bflixChipperKey = base64DecodeAPI("Yjc=ejM=TzA=YTk=WHE=WnU=bXU=RFo=") const val bflixKey = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" val encodedIndex = arrayOf( @@ -484,158 +488,29 @@ suspend fun invokeSmashyFfix( } -suspend fun invokeSmashyGtop( +suspend fun invokeSmashyFm( name: String, url: String, - callback: (ExtractorLink) -> Unit -) { - val doc = app.get(url).document - val script = doc.selectFirst("script:containsData(var secret)")?.data() ?: return - val secret = - script.substringAfter("secret = \"").substringBefore("\";").let { base64Decode(it) } - val key = script.substringAfter("token = \"").substringBefore("\";") - val source = app.get( - "$secret$key", - headers = mapOf( - "X-Requested-With" to "XMLHttpRequest" - ) - ).parsedSafe() ?: return - - val videoUrl = base64Decode(source.file ?: return) - if (videoUrl.contains("/bug")) return - val quality = - Regex("(\\d{3,4})[Pp]").find(videoUrl)?.groupValues?.getOrNull(1)?.toIntOrNull() - ?: Qualities.P720.value - callback.invoke( - ExtractorLink( - "Smashy [$name]", - "Smashy [$name]", - videoUrl, - "", - quality, - videoUrl.contains(".m3u8") - ) - ) -} - -suspend fun invokeSmashyDude( - name: String, - url: String, - callback: (ExtractorLink) -> Unit -) { - val script = - app.get(url).document.selectFirst("script:containsData(player =)")?.data() ?: return - - val source = Regex("file:\\s*(\\[.*]),").find(script)?.groupValues?.get(1) ?: return - - tryParseJson>(source)?.filter { it.title == "English" }?.map { - M3u8Helper.generateM3u8( - "Smashy [Player 2]", - it.file ?: return@map, - "" - ).forEach(callback) - } - -} - -suspend fun invokeSmashyRip( - name: String, - url: String, - subtitleCallback: (SubtitleFile) -> Unit, + ref: String, callback: (ExtractorLink) -> Unit, ) { - val script = - app.get(url).document.selectFirst("script:containsData(player =)")?.data() ?: return - - val source = Regex("file:\\s*\"([^\"]+)").find(script)?.groupValues?.get(1) - val subtitle = Regex("subtitle:\\s*\"([^\"]+)").find(script)?.groupValues?.get(1) - - source?.split(",")?.map { links -> - val quality = Regex("\\[(\\d+)]").find(links)?.groupValues?.getOrNull(1)?.trim() - val link = links.removePrefix("[$quality]").substringAfter("dev/").trim() - if (link.isEmpty()) return@map - callback.invoke( - ExtractorLink( - "Smashy [$name]", - "Smashy [$name]", - link, - "", - quality?.toIntOrNull() ?: return@map, - isM3u8 = true, - ) - ) + fun String.removeProxy(): String { + return if (this.contains("proxy")) { + "https${this.substringAfterLast("https")}" + } else { + this + } } - subtitle?.replace("
", "")?.split(",")?.map { sub -> - val lang = Regex("\\[(.*?)]").find(sub)?.groupValues?.getOrNull(1)?.trim() - val link = sub.removePrefix("[$lang]") - subtitleCallback.invoke( - SubtitleFile( - lang.orEmpty().ifEmpty { return@map }, - link - ) - ) - } - -} - -suspend fun invokeSmashyIm( - name: String, - url: String, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit, -) { - val script = - app.get(url).document.selectFirst("script:containsData(player =)")?.data() ?: return - - val sources = - Regex("['\"]?file['\"]?:\\s*\"([^\"]+)").find(script)?.groupValues?.get(1) ?: return - val subtitles = - Regex("['\"]?subtitle['\"]?:\\s*\"([^\"]+)").find(script)?.groupValues?.get(1) ?: return + val res = app.get(url, referer = ref).text + val source = Regex("['\"]?file['\"]?:\\s*\"([^\"]+)").find(res)?.groupValues?.get(1) ?: return M3u8Helper.generateM3u8( "Smashy [$name]", - sources, - "" + source.removeProxy(), + "https://vidstream.pro/" ).forEach(callback) - subtitles.split(",").map { sub -> - val lang = Regex("\\[(.*?)]").find(sub)?.groupValues?.getOrNull(1)?.trim() - val trimmedSubLink = sub.removePrefix("[$lang]").trim().substringAfter("?url=") - subtitleCallback.invoke( - SubtitleFile( - lang.takeIf { !it.isNullOrEmpty() } ?: return@map, - trimmedSubLink - ) - ) - } - -} - -suspend fun invokeSmashyRw( - name: String, - url: String, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit, -) { - val res = app.get(url).document - val video = res.selectFirst("media-player")?.attr("src") - - M3u8Helper.generateM3u8( - "Smashy [$name]", - video ?: return, - "" - ).forEach(callback) - - res.select("track").map { track -> - subtitleCallback.invoke( - SubtitleFile( - track.attr("label"), - track.attr("src"), - ) - ) - } - } suspend fun getDumpIdAndType(title: String?, year: Int?, season: Int?): Pair { @@ -696,6 +571,52 @@ suspend fun fetchDumpEpisodes(id: String, type: String, episode: Int?): EpisodeV } } +suspend fun invokeDrivetot( + url: String, + tags: String? = null, + size: String? = null, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit, +) { + val res = app.get(url) + val data = res.document.select("form input").associate { it.attr("name") to it.attr("value") } + app.post(res.url, data = data, cookies = res.cookies).document.select("div.card-body a").apmap { ele -> + val href = base64Decode(ele.attr("href").substringAfterLast("/")).let { + if(it.contains("hubcloud.lol")) it.replace("hubcloud.lol", "hubcloud.in") else it + } + loadExtractor(href, "$hdmovies4uAPI/", subtitleCallback) { link -> + callback.invoke( + ExtractorLink( + link.source, + "${link.name} $tags [$size]", + link.url, + link.referer, + link.quality, + link.type, + link.headers, + link.extractorData + ) + ) + } + } +} + +suspend fun bypassBqrecipes(url: String): String? { + var res = app.get(url) + var location = res.text.substringAfter(".replace('").substringBefore("');") + var cookies = res.cookies + res = app.get(location, cookies = cookies) + cookies = cookies + res.cookies + val document = res.document + location = document.select("form#recaptcha").attr("action") + val data = + document.select("form#recaptcha input").associate { it.attr("name") to it.attr("value") } + res = app.post(location, data = data, cookies = cookies) + location = res.document.selectFirst("a#messagedown")?.attr("href") ?: return null + cookies = (cookies + res.cookies).minus("var") + return app.get(location, cookies = cookies, allowRedirects = false).headers["location"] +} + suspend fun bypassOuo(url: String?): String? { var res = session.get(url ?: return null) run lit@{ @@ -915,8 +836,6 @@ suspend fun getTvMoviesServer(url: String, season: Int?, episode: Int?): Pair? = null suspend fun getFilmxyCookies(url: String) = filmxyCookies ?: fetchFilmxyCookies(url).also { filmxyCookies = it } suspend fun fetchFilmxyCookies(url: String): Map { @@ -957,6 +876,21 @@ suspend fun fetchFilmxyCookies(url: String): Map { return cookieJar.plus(defaultCookies) } +suspend fun getWatchflxCookies() = watchflxCookies ?: fetchWatchflxCookies().also { watchflxCookies = it } + +suspend fun fetchWatchflxCookies(): Map { + session.get(watchflxAPI) + val cookies = session.baseClient.cookieJar.loadForRequest(watchflxAPI.toHttpUrl()) + .associate { it.name to it.value } + val loginUrl = "$watchflxAPI/cookie-based-login" + session.post( + loginUrl, data = mapOf( + "continue_as_temp" to "true" + ), cookies = cookies, headers = mapOf("X-Requested-With" to "XMLHttpRequest") + ) + return session.baseClient.cookieJar.loadForRequest(loginUrl.toHttpUrl()).associate { it.name to it.value } +} + fun Document.findTvMoviesIframe(): String? { return this.selectFirst("script:containsData(var seconds)")?.data()?.substringAfter("href='") ?.substringBefore("'>")