diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 03c8efdc..b5e17d0d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -43,10 +43,6 @@ jobs: - name: Access Secrets env: TMDB_API: ${{ secrets.TMDB_API }} - SORA_API: ${{ secrets.SORA_API }} - SORAHE: ${{ secrets.SORAHE }} - SORAXA: ${{ secrets.SORAXA }} - SORATED: ${{ secrets.SORATED }} DUMP_API: ${{ secrets.DUMP_API }} DUMP_KEY: ${{ secrets.DUMP_KEY }} CRUNCHYROLL_BASIC_TOKEN: ${{ secrets.CRUNCHYROLL_BASIC_TOKEN }} @@ -55,16 +51,13 @@ jobs: ANICHI_SERVER: ${{ secrets.ANICHI_SERVER }} ANICHI_ENDPOINT: ${{ secrets.ANICHI_ENDPOINT }} ANICHI_APP: ${{ secrets.ANICHI_APP }} - PRIMEWIRE_KEY: ${{ secrets.PRIMEWIRE_KEY }} ZSHOW_API: ${{ secrets.ZSHOW_API }} SFMOVIES_API: ${{ secrets.SFMOVIES_API }} + CINEMATV_API: ${{ secrets.CINEMATV_API }} + OMOVIES_API: ${{ secrets.OMOVIES_API }} run: | cd $GITHUB_WORKSPACE/src - echo SORA_API=$SORA_API >> local.properties echo TMDB_API=$TMDB_API >> local.properties - echo SORAHE=$SORAHE >> local.properties - echo SORAXA=$SORAXA >> local.properties - echo SORATED=$SORATED >> local.properties echo DUMP_API=$DUMP_API >> local.properties echo DUMP_KEY=$DUMP_KEY >> local.properties echo CRUNCHYROLL_BASIC_TOKEN=$CRUNCHYROLL_BASIC_TOKEN >> local.properties @@ -73,9 +66,10 @@ jobs: echo ANICHI_SERVER=$ANICHI_SERVER >> local.properties echo ANICHI_ENDPOINT=$ANICHI_ENDPOINT >> local.properties echo ANICHI_APP=$ANICHI_APP >> local.properties - echo PRIMEWIRE_KEY=$PRIMEWIRE_KEY >> local.properties echo ZSHOW_API=$ZSHOW_API >> local.properties echo SFMOVIES_API=$SFMOVIES_API >> local.properties + echo CINEMATV_API=$CINEMATV_API >> local.properties + echo OMOVIES_API=$OMOVIES_API >> local.properties - name: Build Plugins run: | @@ -85,10 +79,10 @@ jobs: cp **/build/*.cs3 $GITHUB_WORKSPACE/builds cp build/plugins.json $GITHUB_WORKSPACE/builds - - name: Move Loklok + - name: Move Kuramanime run: | - rm $GITHUB_WORKSPACE/builds/Loklok.cs3 || true - cp $GITHUB_WORKSPACE/builds/stored/Loklok.cs3 $GITHUB_WORKSPACE/builds + rm $GITHUB_WORKSPACE/builds/KuramanimeProvider.cs3 || true + cp $GITHUB_WORKSPACE/builds/stored/KuramanimeProvider.cs3 $GITHUB_WORKSPACE/builds - name: Push builds run: | diff --git a/Animasu/build.gradle.kts b/Animasu/build.gradle.kts index 338aaa8a..c2504dae 100644 --- a/Animasu/build.gradle.kts +++ b/Animasu/build.gradle.kts @@ -1,5 +1,5 @@ // use an integer for version numbers -version = 6 +version = 8 cloudstream { diff --git a/Animasu/src/main/kotlin/com/hexated/Animasu.kt b/Animasu/src/main/kotlin/com/hexated/Animasu.kt index 25129feb..4e726b6e 100644 --- a/Animasu/src/main/kotlin/com/hexated/Animasu.kt +++ b/Animasu/src/main/kotlin/com/hexated/Animasu.kt @@ -138,7 +138,7 @@ class Animasu : MainAPI() { document.select(".mobius > .mirror > option").mapNotNull { fixUrl(Jsoup.parse(base64Decode(it.attr("value"))).select("iframe").attr("src")) to it.text() }.apmap { (iframe, quality) -> - loadFixedExtractor(iframe, quality, "$mainUrl/", subtitleCallback, callback) + loadFixedExtractor(iframe.fixIframe(), quality, "$mainUrl/", subtitleCallback, callback) } return true } @@ -157,7 +157,7 @@ class Animasu : MainAPI() { link.name, link.url, link.referer, - if(link.type != ExtractorLinkType.M3U8) getIndexQuality(quality) else link.quality, + if(link.type == ExtractorLinkType.M3U8 || link.name == "Uservideo") link.quality else getIndexQuality(quality), link.type, link.headers, link.extractorData @@ -166,6 +166,14 @@ class Animasu : MainAPI() { } } + private fun String.fixIframe() : String { + return if(this.startsWith("https://dl.berkasdrive.com")) { + base64Decode(this.substringAfter("id=")) + } else { + this + } + } + private fun getIndexQuality(str: String?): Int { return Regex("(\\d{3,4})[pP]").find(str ?: "")?.groupValues?.getOrNull(1)?.toIntOrNull() ?: Qualities.Unknown.value diff --git a/AnimeIndoProvider/src/main/kotlin/com/hexated/AnimeIndoProvider.kt b/AnimeIndoProvider/src/main/kotlin/com/hexated/AnimeIndoProvider.kt index af29b007..fcf6489c 100644 --- a/AnimeIndoProvider/src/main/kotlin/com/hexated/AnimeIndoProvider.kt +++ b/AnimeIndoProvider/src/main/kotlin/com/hexated/AnimeIndoProvider.kt @@ -4,12 +4,9 @@ import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.LoadResponse.Companion.addAniListId import com.lagradost.cloudstream3.LoadResponse.Companion.addMalId import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer -import com.lagradost.cloudstream3.network.CloudflareKiller import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.httpsify import com.lagradost.cloudstream3.utils.loadExtractor -import okhttp3.Interceptor -import okhttp3.Response import org.jsoup.Jsoup import org.jsoup.nodes.Element @@ -18,26 +15,12 @@ class AnimeIndoProvider : MainAPI() { override var name = "AnimeIndo" override val hasMainPage = true override var lang = "id" - private val cloudflareKiller by lazy { CloudflareKiller() } - private val interceptor by lazy { CloudflareInterceptor(cloudflareKiller) } override val supportedTypes = setOf( TvType.Anime, TvType.AnimeMovie, TvType.OVA ) - class CloudflareInterceptor(private val cloudflareKiller: CloudflareKiller): Interceptor { - override fun intercept(chain: Interceptor.Chain): Response { - val request = chain.request() - val response = chain.proceed(request) - val doc = Jsoup.parse(response.peekBody(1024 * 1024).string()) - if (doc.select("title").text() == "Just a moment...") { - return cloudflareKiller.intercept(chain) - } - return response - } - } - companion object { fun getType(t: String): TvType { return if (t.contains("OVA", true) || t.contains("Special")) TvType.OVA @@ -66,8 +49,7 @@ class AnimeIndoProvider : MainAPI() { page: Int, request: MainPageRequest ): HomePageResponse { - val url = "$mainUrl/${request.data}/page/$page" - val document = app.get(url, interceptor = interceptor).document + val document = app.get("$mainUrl/${request.data}/page/$page").document val home = document.select("main#main div.animposx").mapNotNull { it.toSearchResult() } @@ -101,7 +83,6 @@ class AnimeIndoProvider : MainAPI() { return newAnimeSearchResponse(title, href, TvType.Anime) { this.posterUrl = posterUrl addSub(epNum) - posterHeaders = cloudflareKiller.getCookieHeaders(mainUrl).toMap() } } @@ -109,8 +90,7 @@ class AnimeIndoProvider : MainAPI() { override suspend fun search(query: String): List { val anime = mutableListOf() (1..2).forEach { page -> - val link = "$mainUrl/page/$page/?s=$query" - val document = app.get(link, interceptor = interceptor).document + val document = app.get("$mainUrl/page/$page/?s=$query").document val media = document.select(".site-main.relat > article").mapNotNull { val title = it.selectFirst("div.title > h2")!!.ownText().trim() val href = it.selectFirst("a")!!.attr("href") @@ -118,7 +98,6 @@ class AnimeIndoProvider : MainAPI() { val type = getType(it.select("div.type").text().trim()) newAnimeSearchResponse(title, href, type) { this.posterUrl = posterUrl - posterHeaders = cloudflareKiller.getCookieHeaders(mainUrl).toMap() } } if(media.isNotEmpty()) anime.addAll(media) @@ -127,8 +106,7 @@ class AnimeIndoProvider : MainAPI() { } override suspend fun load(url: String): LoadResponse? { - val document = app.get(url, interceptor = interceptor).document - + val document = app.get(url).document val title = document.selectFirst("h1.entry-title")?.text()?.replace("Subtitle Indonesia", "") ?.trim() ?: return null val poster = document.selectFirst("div.thumb > img[itemprop=image]")?.attr("src") @@ -168,7 +146,6 @@ class AnimeIndoProvider : MainAPI() { addTrailer(trailer) addMalId(tracker?.malId) addAniListId(tracker?.aniId?.toIntOrNull()) - posterHeaders = cloudflareKiller.getCookieHeaders(mainUrl).toMap() } } @@ -179,12 +156,12 @@ class AnimeIndoProvider : MainAPI() { callback: (ExtractorLink) -> Unit ): Boolean { - val document = app.get(data, interceptor = interceptor).document + val document = app.get(data).document document.select("div.itemleft > .mirror > option").mapNotNull { fixUrl(Jsoup.parse(base64Decode(it.attr("value"))).select("iframe").attr("src")) }.apmap { if (it.startsWith(mainUrl)) { - app.get(it, referer = "$mainUrl/", interceptor = interceptor).document.select("iframe").attr("src") + app.get(it, referer = "$mainUrl/").document.select("iframe").attr("src") } else { it } diff --git a/Gomov/build.gradle.kts b/Gomov/build.gradle.kts index a19bfe05..dd797cdd 100644 --- a/Gomov/build.gradle.kts +++ b/Gomov/build.gradle.kts @@ -1,7 +1,7 @@ import org.jetbrains.kotlin.konan.properties.Properties // use an integer for version numbers -version = 28 +version = 29 android { defaultConfig { diff --git a/Gomov/src/main/kotlin/com/hexated/DutaMovie.kt b/Gomov/src/main/kotlin/com/hexated/DutaMovie.kt index d18f56e7..270bf211 100644 --- a/Gomov/src/main/kotlin/com/hexated/DutaMovie.kt +++ b/Gomov/src/main/kotlin/com/hexated/DutaMovie.kt @@ -6,8 +6,12 @@ import com.lagradost.cloudstream3.utils.httpsify import com.lagradost.cloudstream3.utils.loadExtractor class DutaMovie : Gomov() { + override var mainUrl = "https://cinema.dutamovie21.vip" - override var name = "DutaMovie" + + + +override var name = "DutaMovie" override val mainPage = mainPageOf( "category/box-office/page/%d/" to "Box Office", "category/serial-tv/page/%d/" to "Serial TV", diff --git a/Gomov/src/main/kotlin/com/hexated/Extractors.kt b/Gomov/src/main/kotlin/com/hexated/Extractors.kt index 35e1bc5d..8cb8cc50 100644 --- a/Gomov/src/main/kotlin/com/hexated/Extractors.kt +++ b/Gomov/src/main/kotlin/com/hexated/Extractors.kt @@ -1,21 +1,14 @@ package com.hexated -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.ErrorLoadingException import com.lagradost.cloudstream3.SubtitleFile import com.lagradost.cloudstream3.apmap import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.base64Decode import com.lagradost.cloudstream3.extractors.* -import com.lagradost.cloudstream3.extractors.helper.AesHelper -import com.lagradost.cloudstream3.utils.AppUtils import com.lagradost.cloudstream3.utils.ExtractorApi import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.M3u8Helper import com.lagradost.cloudstream3.utils.loadExtractor -import org.jsoup.nodes.Document -import org.mozilla.javascript.Context -import org.mozilla.javascript.Scriptable open class Uplayer : ExtractorApi() { override val name = "Uplayer" @@ -94,81 +87,4 @@ class Likessb : StreamSB() { class DbGdriveplayer : Gdriveplayer() { override var mainUrl = "https://database.gdriveplayer.us" -} - -object NineTv { - - suspend fun getUrl( - url: String, - referer: String?, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ) { - val mainUrl = getBaseUrl(url) - val res = app.get(url, headers = mapOf( - "Accept" to "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8", - "Accept-Language" to "en-US,en;q=0.5", - "Referer" to (referer ?: ""), - )) - val master = Regex("\\s*=\\s*'([^']+)").find(res.text)?.groupValues?.get(1) - val key = "tSIsE8FgpRkv3QQQ" - val decrypt = AesHelper.cryptoAESHandler(master ?: return, key.toByteArray(), false) - ?.replace("\\", "") - ?: throw ErrorLoadingException("failed to decrypt") - - val source = Regex(""""?file"?:\s*"([^"]+)""").find(decrypt)?.groupValues?.get(1) - val tracks = Regex("""tracks:\s*\[(.+)]""").find(decrypt)?.groupValues?.get(1) - val name = url.getHost() - - M3u8Helper.generateM3u8( - name, - source ?: return, - "$mainUrl/", - headers = mapOf( - "Accept" to "*/*", - "Connection" to "keep-alive", - "Sec-Fetch-Dest" to "empty", - "Sec-Fetch-Mode" to "cors", - "Sec-Fetch-Site" to "cross-site", - "Origin" to mainUrl, - ) - ).forEach(callback) - - AppUtils.tryParseJson>("[$tracks]") - ?.filter { it.kind == "captions" }?.map { track -> - subtitleCallback.invoke( - SubtitleFile( - track.label ?: "", - track.file ?: return@map null - ) - ) - } - } - - private fun Document.getKeys(): String? { - val script = (this.selectFirst("script:containsData(eval\\()")?.data() - ?.replace("eval(", "var result=")?.removeSuffix(");") + ";").trimIndent() - val run = script.runJS("result") - return """,\s*'([^']+)""".toRegex().find(run)?.groupValues?.getOrNull(1) - } - - private fun String.runJS(variable: String): String { - val rhino = Context.enter() - rhino.optimizationLevel = -1 - val scope: Scriptable = rhino.initSafeStandardObjects() - val result: String - try { - rhino.evaluateString(scope, this, "JavaScript", 1, null) - result = Context.toString(scope.get(variable, scope)) - } finally { - Context.exit() - } - return result - } - - data class Tracks( - @JsonProperty("file") val file: String? = null, - @JsonProperty("label") val label: String? = null, - @JsonProperty("kind") val kind: String? = null, - ) } \ No newline at end of file diff --git a/Gomov/src/main/kotlin/com/hexated/Gomov.kt b/Gomov/src/main/kotlin/com/hexated/Gomov.kt index 948686b9..b3eadaf1 100644 --- a/Gomov/src/main/kotlin/com/hexated/Gomov.kt +++ b/Gomov/src/main/kotlin/com/hexated/Gomov.kt @@ -20,7 +20,6 @@ open class Gomov : MainAPI() { TvType.TvSeries, TvType.AsianDrama ) - private val sources = arrayOf("https://chillx.top", "https://watchx.top", "https://bestx.stream") override val mainPage = mainPageOf( "page/%d/?s&search=advanced&post_type=movie" to "Movies", @@ -46,7 +45,7 @@ open class Gomov : MainAPI() { private fun Element.toSearchResult(): SearchResponse? { val title = this.selectFirst("h2.entry-title > a")?.text()?.trim() ?: return null val href = fixUrl(this.selectFirst("a")!!.attr("href")) - val posterUrl = fixUrlNull(this.selectFirst("a > img").getImgAttr()).fixImageQuality() + val posterUrl = fixUrlNull(this.selectFirst("a > img")?.getImageAttr()).fixImageQuality() val quality = this.select("div.gmr-qual, div.gmr-quality-item > a").text().trim().replace("-", "") return if (quality.isEmpty()) { val episode = @@ -67,7 +66,7 @@ open class Gomov : MainAPI() { private fun Element.toRecommendResult(): SearchResponse? { val title = this.selectFirst("a > span.idmuvi-rp-title")?.text()?.trim() ?: return null val href = this.selectFirst("a")!!.attr("href") - val posterUrl = fixUrlNull(this.selectFirst("a > img").getImgAttr().fixImageQuality()) + val posterUrl = fixUrlNull(this.selectFirst("a > img")?.getImageAttr().fixImageQuality()) return newMovieSearchResponse(title, href, TvType.Movie) { this.posterUrl = posterUrl } @@ -89,7 +88,7 @@ open class Gomov : MainAPI() { document.selectFirst("h1.entry-title")?.text()?.substringBefore("Season")?.substringBefore("Episode")?.trim() .toString() val poster = - fixUrlNull(document.selectFirst("figure.pull-left > img").getImgAttr())?.fixImageQuality() + fixUrlNull(document.selectFirst("figure.pull-left > img")?.getImageAttr())?.fixImageQuality() val tags = document.select("span.gmr-movie-genre:contains(Genre:) > a").map { it.text() } val year = @@ -159,12 +158,7 @@ open class Gomov : MainAPI() { val iframe = app.get(fixUrl(ele.attr("href"))).document.selectFirst("div.gmr-embed-responsive iframe") .getIframeAttr()?.let { httpsify(it) } ?: return@apmap - when { - sources.any { iframe.startsWith(it) } -> NineTv.getUrl(iframe, "$directUrl/", subtitleCallback, callback) - else -> { - loadExtractor(iframe, "$directUrl/", subtitleCallback, callback) - } - } + loadExtractor(iframe, "$directUrl/", subtitleCallback, callback) } } else { document.select("div.tab-content-ajax").apmap { ele -> @@ -173,12 +167,7 @@ open class Gomov : MainAPI() { data = mapOf("action" to "muvipro_player_content", "tab" to ele.attr("id"), "post_id" to "$id") ).document.select("iframe").attr("src").let { httpsify(it) } - when { - sources.any { server.startsWith(it) } -> NineTv.getUrl(server, "$directUrl/", subtitleCallback, callback) - else -> { - loadExtractor(server, "$directUrl/", subtitleCallback, callback) - } - } + loadExtractor(server, "$directUrl/", subtitleCallback, callback) } } @@ -187,28 +176,29 @@ open class Gomov : MainAPI() { } - private fun Element?.getImgAttr() : String? { - return this?.attr("data-src").takeIf { it?.isNotEmpty() == true } ?: this?.attr("src") + private fun Element.getImageAttr(): String? { + return when { + this.hasAttr("data-src") -> this.attr("abs:data-src") + this.hasAttr("data-lazy-src") -> this.attr("abs:data-lazy-src") + this.hasAttr("srcset") -> this.attr("abs:srcset").substringBefore(" ") + else -> this.attr("abs:src") + } } private fun Element?.getIframeAttr() : String? { return this?.attr("data-litespeed-src").takeIf { it?.isNotEmpty() == true } ?: this?.attr("src") } -} - -fun String?.fixImageQuality(): String? { - if (this == null) return null - val regex = Regex("(-\\d*x\\d*)").find(this)?.groupValues?.get(0) ?: return this - return this.replace(regex, "") -} - -fun getBaseUrl(url: String): String { - return URI(url).let { - "${it.scheme}://${it.host}" + private fun String?.fixImageQuality(): String? { + if (this == null) return null + val regex = Regex("(-\\d*x\\d*)").find(this)?.groupValues?.get(0) ?: return this + return this.replace(regex, "") + } + + private fun getBaseUrl(url: String): String { + return URI(url).let { + "${it.scheme}://${it.host}" + } } -} -fun String.getHost(): String { - return fixTitle(URI(this).host.substringBeforeLast(".").substringAfterLast(".")) } \ No newline at end of file diff --git a/Gomov/src/main/kotlin/com/hexated/Ngefilm.kt b/Gomov/src/main/kotlin/com/hexated/Ngefilm.kt index 4e2f44ca..3ae7ed45 100644 --- a/Gomov/src/main/kotlin/com/hexated/Ngefilm.kt +++ b/Gomov/src/main/kotlin/com/hexated/Ngefilm.kt @@ -3,7 +3,10 @@ package com.hexated import com.lagradost.cloudstream3.mainPageOf class Ngefilm : Gomov() { + override var mainUrl = "https://nge-film21.fun" + + override var name = "Ngefilm" override val mainPage = mainPageOf( diff --git a/Gomov/src/main/kotlin/com/hexated/Nodrakorid.kt b/Gomov/src/main/kotlin/com/hexated/Nodrakorid.kt index db3a7845..0597c5c1 100644 --- a/Gomov/src/main/kotlin/com/hexated/Nodrakorid.kt +++ b/Gomov/src/main/kotlin/com/hexated/Nodrakorid.kt @@ -9,7 +9,7 @@ import org.jsoup.nodes.Element import java.net.URI class Nodrakorid : Gomov() { - override var mainUrl = "https://no-drakor.xyz" + override var mainUrl = "https://nodra-kor.xyz" override var name = "Nodrakorid" override val mainPage = mainPageOf( diff --git a/KuramanimeProvider/build.gradle.kts b/KuramanimeProvider/build.gradle.kts index ed7addec..fac8e2b3 100644 --- a/KuramanimeProvider/build.gradle.kts +++ b/KuramanimeProvider/build.gradle.kts @@ -1,5 +1,5 @@ // use an integer for version numbers -version = 23 +version = 34 cloudstream { diff --git a/KuramanimeProvider/src/main/kotlin/com/hexated/Extractors.kt b/KuramanimeProvider/src/main/kotlin/com/hexated/Extractors.kt index 521502ac..2b55fd0d 100644 --- a/KuramanimeProvider/src/main/kotlin/com/hexated/Extractors.kt +++ b/KuramanimeProvider/src/main/kotlin/com/hexated/Extractors.kt @@ -1,7 +1,14 @@ package com.hexated +import com.fasterxml.jackson.annotation.JsonProperty +import com.lagradost.cloudstream3.SubtitleFile +import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.extractors.Filesim import com.lagradost.cloudstream3.extractors.StreamSB +import com.lagradost.cloudstream3.utils.ExtractorApi +import com.lagradost.cloudstream3.utils.ExtractorLink +import com.lagradost.cloudstream3.utils.Qualities +import com.lagradost.cloudstream3.utils.getQualityFromName class Nyomo : StreamSB() { override var name: String = "Nyomo" @@ -11,4 +18,101 @@ class Nyomo : StreamSB() { class Streamhide : Filesim() { override var name: String = "Streamhide" override var mainUrl: String = "https://streamhide.to" +} + +open class Lbx : ExtractorApi() { + override val name = "Linkbox" + override val mainUrl = "https://lbx.to" + private val realUrl = "https://www.linkbox.to" + override val requiresReferer = true + + override suspend fun getUrl( + url: String, + referer: String?, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ) { + val token = Regex("""(?:/f/|/file/|\?id=)(\w+)""").find(url)?.groupValues?.get(1) + val id = app.get("$realUrl/api/file/share_out_list/?sortField=utime&sortAsc=0&pageNo=1&pageSize=50&shareToken=$token").parsedSafe()?.data?.itemId + app.get("$realUrl/api/file/detail?itemId=$id", referer = url) + .parsedSafe()?.data?.itemInfo?.resolutionList?.map { link -> + callback.invoke( + ExtractorLink( + name, + name, + link.url ?: return@map null, + "$realUrl/", + getQualityFromName(link.resolution) + ) + ) + } + } + + data class Resolutions( + @JsonProperty("url") val url: String? = null, + @JsonProperty("resolution") val resolution: String? = null, + ) + + data class ItemInfo( + @JsonProperty("resolutionList") val resolutionList: ArrayList? = arrayListOf(), + ) + + data class Data( + @JsonProperty("itemInfo") val itemInfo: ItemInfo? = null, + @JsonProperty("itemId") val itemId: String? = null, + ) + + data class Responses( + @JsonProperty("data") val data: Data? = null, + ) + +} + +open class Kuramadrive : ExtractorApi() { + override val name = "DriveKurama" + override val mainUrl = "https://kuramadrive.com" + override val requiresReferer = true + + override suspend fun getUrl( + url: String, + referer: String?, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ) { + val req = app.get(url, referer = referer) + val doc = req.document + + val title = doc.select("title").text() + val token = doc.select("meta[name=csrf-token]").attr("content") + val routeCheckAvl = doc.select("input#routeCheckAvl").attr("value") + + val json = app.get( + routeCheckAvl, headers = mapOf( + "X-Requested-With" to "XMLHttpRequest", + "X-CSRF-TOKEN" to token + ), + referer = url, + cookies = req.cookies + ).parsedSafe() + + callback.invoke( + ExtractorLink( + name, + name, + json?.url ?: return, + "$mainUrl/", + getIndexQuality(title), + ) + ) + } + + private fun getIndexQuality(str: String?): Int { + return Regex("(\\d{3,4})[pP]").find(str ?: "")?.groupValues?.getOrNull(1)?.toIntOrNull() + ?: Qualities.Unknown.value + } + + private data class Source( + @JsonProperty("url") val url: String, + ) + } \ No newline at end of file diff --git a/KuramanimeProvider/src/main/kotlin/com/hexated/KuramanimeProvider.kt b/KuramanimeProvider/src/main/kotlin/com/hexated/KuramanimeProvider.kt index b9805a2d..82d488e4 100644 --- a/KuramanimeProvider/src/main/kotlin/com/hexated/KuramanimeProvider.kt +++ b/KuramanimeProvider/src/main/kotlin/com/hexated/KuramanimeProvider.kt @@ -8,6 +8,8 @@ import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.Qualities import com.lagradost.cloudstream3.utils.loadExtractor import com.lagradost.nicehttp.requestCreator +import okhttp3.Headers +import okhttp3.HttpUrl import org.jsoup.Jsoup import org.jsoup.nodes.Element @@ -18,7 +20,7 @@ class KuramanimeProvider : MainAPI() { override val hasMainPage = true override var lang = "id" override val hasDownloadSupport = true - private var auth: String? = null + private var params: AuthParams? = null private var headers: Map = mapOf() private var cookies: Map = mapOf() override val supportedTypes = setOf( @@ -199,67 +201,70 @@ class KuramanimeProvider : MainAPI() { val req = app.get(data) val res = req.document - val token = res.select("meta[name=csrf-token]").attr("content") - headers = mapOf( - "Accept" to "application/json, text/javascript, */*; q=0.01", - "Authorization" to "${getAuth(data)}", - "X-Requested-With" to "XMLHttpRequest", - "X-CSRF-TOKEN" to token - ) - cookies = req.cookies - res.select("select#changeServer option").apmap { source -> - val server = source.attr("value") - val link = "$data?dfgRr1OagZvvxbzHNpyCy0FqJQ18mCnb=${getMisc()}&twEvZlbZbYRWBdKKwxkOnwYF0VWoGGVg=$server" - if (server.contains(Regex("(?i)kuramadrive|archive"))) { - invokeLocalSource(link, server, data, callback) - } else { - app.get( - link, - referer = data, - headers = headers, - cookies = cookies - ).document.select("div.iframe-container iframe").attr("src").let { videoUrl -> - loadExtractor(fixUrl(videoUrl), "$mainUrl/", subtitleCallback, callback) + + argamap( + { + val auth = getAuth(data) + headers = auth.authHeader?.associate { it.first to it.second }?.filter { it.key != "Cookie" }!! + cookies = req.cookies + res.select("select#changeServer option").apmap { source -> + val server = source.attr("value") + val query = auth.serverUrl?.queryParameterNames?.map { it } ?: return@apmap + val link = "$data?${query[0]}=${getMisc(auth.authUrl)}&${query[1]}=$server" + if (server.contains(Regex("(?i)kuramadrive|archive"))) { + invokeLocalSource(link, server, data, callback) + } else { + app.get( + link, + referer = data, + headers = headers, + cookies = cookies + ).document.select("div.iframe-container iframe").attr("src").let { videoUrl -> + loadExtractor(fixUrl(videoUrl), "$mainUrl/", subtitleCallback, callback) + } + } + } + }, + { + res.select("div#animeDownloadLink a").apmap { + loadExtractor(it.attr("href"), "$mainUrl/", subtitleCallback, callback) } } - } + ) return true } - private fun getAuth() : String { - val key = "kuramanime3:LEcXGYdOGcMCV8jM5fhRdM2mneSj6kaNts:${APIHolder.unixTimeMS};" - return base64Encode(base64Encode(key.toByteArray()).toByteArray()) - } - - private suspend fun fetchAuth(url: String) : String? { + private suspend fun fetchAuth(url: String) : AuthParams { + val regex = Regex("""$mainUrl/\S+""") val found = WebViewResolver( - Regex("$mainUrl/misc/post/EVhcpMNbO77acNZcHr2XVjaG8WAdNC1u") + Regex("""$url(?!\?page=)\?"""), + additionalUrls = listOf(regex) ).resolveUsingWebView( requestCreator( "GET", url ) - ).first - return found?.headers?.get("Authorization") + ) + val addition = found.second.findLast { it.headers["X-Requested-With"] == "XMLHttpRequest" } + return AuthParams(found.first?.url, addition?.url.toString(), addition?.headers) } - private suspend fun getAuth(url: String) = auth ?: fetchAuth(url) + private suspend fun getAuth(url: String) = params ?: fetchAuth(url).also { params = it } - private suspend fun getMisc(): String { + private suspend fun getMisc(url: String?): String { val misc = app.get( - "$mainUrl/misc/post/EVhcpMNbO77acNZcHr2XVjaG8WAdNC1u", - headers = headers + mapOf("X-Request-ID" to getRequestId()), + "$url", + headers = headers, cookies = cookies ) cookies = misc.cookies return misc.parsed() } - private fun getRequestId(length: Int = 8): String { - val allowedChars = ('a'..'z') + ('0'..'9') - return (1..length) - .map { allowedChars.random() } - .joinToString("") - } + data class AuthParams ( + val serverUrl: HttpUrl?, + val authUrl: String?, + val authHeader: Headers?, + ) } \ No newline at end of file diff --git a/KuramanimeProvider/src/main/kotlin/com/hexated/KuramanimeProviderPlugin.kt b/KuramanimeProvider/src/main/kotlin/com/hexated/KuramanimeProviderPlugin.kt index da4282eb..77db0c6e 100644 --- a/KuramanimeProvider/src/main/kotlin/com/hexated/KuramanimeProviderPlugin.kt +++ b/KuramanimeProvider/src/main/kotlin/com/hexated/KuramanimeProviderPlugin.kt @@ -12,5 +12,7 @@ class KuramanimeProviderPlugin: Plugin() { registerMainAPI(KuramanimeProvider()) registerExtractorAPI(Nyomo()) registerExtractorAPI(Streamhide()) + registerExtractorAPI(Kuramadrive()) + registerExtractorAPI(Lbx()) } } \ No newline at end of file diff --git a/Minioppai/build.gradle.kts b/Minioppai/build.gradle.kts index ee49ee06..87203b16 100644 --- a/Minioppai/build.gradle.kts +++ b/Minioppai/build.gradle.kts @@ -1,5 +1,5 @@ // use an integer for version numbers -version = 8 +version = 9 cloudstream { diff --git a/Minioppai/src/main/kotlin/com/hexated/Extractors.kt b/Minioppai/src/main/kotlin/com/hexated/Extractors.kt index 6a82e5d3..3e172453 100644 --- a/Minioppai/src/main/kotlin/com/hexated/Extractors.kt +++ b/Minioppai/src/main/kotlin/com/hexated/Extractors.kt @@ -44,10 +44,12 @@ open class Streampai : ExtractorApi() { this.name, this.name, fixUrl(it.file), - "$mainUrl/", + url, getQualityFromName(it.label), headers = mapOf( "Accept" to "video/webm,video/ogg,video/*;q=0.9,application/ogg;q=0.7,audio/*;q=0.6,*/*;q=0.5", + "Accept-Language" to "en-US,en;q=0.5", + "DNT" to "1", "Range" to "bytes=0-", "Sec-Fetch-Dest" to "video", "Sec-Fetch-Mode" to "no-cors", diff --git a/Minioppai/src/main/kotlin/com/hexated/Minioppai.kt b/Minioppai/src/main/kotlin/com/hexated/Minioppai.kt index 2dc220a1..b7a2a906 100644 --- a/Minioppai/src/main/kotlin/com/hexated/Minioppai.kt +++ b/Minioppai/src/main/kotlin/com/hexated/Minioppai.kt @@ -2,10 +2,7 @@ package com.hexated import com.fasterxml.jackson.annotation.JsonProperty import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.network.CloudflareKiller import com.lagradost.cloudstream3.utils.* -import okhttp3.Interceptor -import okhttp3.Response import org.jsoup.Jsoup import org.jsoup.nodes.Element import java.net.URLDecoder @@ -17,24 +14,10 @@ class Minioppai : MainAPI() { override var lang = "id" override val hasDownloadSupport = true override val hasQuickSearch = true - private val cloudflareKiller by lazy { CloudflareKiller() } - private val interceptor by lazy { CloudflareInterceptor(cloudflareKiller) } override val supportedTypes = setOf( TvType.NSFW, ) - class CloudflareInterceptor(private val cloudflareKiller: CloudflareKiller): Interceptor { - override fun intercept(chain: Interceptor.Chain): Response { - val request = chain.request() - val response = chain.proceed(request) - val doc = Jsoup.parse(response.peekBody(1024 * 1024).string()) - if (doc.select("title").text() == "Just a moment...") { - return cloudflareKiller.intercept(chain) - } - return response - } - } - companion object { fun getStatus(t: String?): ShowStatus { return when (t) { @@ -54,7 +37,7 @@ class Minioppai : MainAPI() { page: Int, request: MainPageRequest ): HomePageResponse { - val document = app.get("${request.data}/page/$page", interceptor = interceptor).document + val document = app.get("${request.data}/page/$page").document val home = document.select("div.latest a").mapNotNull { it.toSearchResult() } @@ -84,7 +67,6 @@ class Minioppai : MainAPI() { return newAnimeSearchResponse(title, href, TvType.NSFW) { this.posterUrl = posterUrl addSub(epNum) - posterHeaders = cloudflareKiller.getCookieHeaders(mainUrl).toMap() } } @@ -97,7 +79,6 @@ class Minioppai : MainAPI() { "action" to "ts_ac_do_search", "ts_ac_query" to query, ), headers = mapOf("X-Requested-With" to "XMLHttpRequest"), - interceptor = interceptor ).parsedSafe()?.post?.firstOrNull()?.all?.mapNotNull { item -> newAnimeSearchResponse( item.postTitle ?: "", @@ -105,13 +86,12 @@ class Minioppai : MainAPI() { TvType.NSFW ) { this.posterUrl = item.postImage - posterHeaders = cloudflareKiller.getCookieHeaders(mainUrl).toMap() } } } override suspend fun load(url: String): LoadResponse? { - val document = app.get(url, interceptor = interceptor).document + val document = app.get(url).document val title = document.selectFirst("h1.entry-title")?.text()?.trim() ?: return null val poster = fixUrlNull(document.selectFirst("div.limage img")?.attr("src")) @@ -127,7 +107,7 @@ class Minioppai : MainAPI() { val name = it.selectFirst("div.epl-num")?.text() val link = fixUrlNull(it.selectFirst("a")?.attr("href")) ?: return@mapNotNull null Episode(link, name = name) - }.reversed() + } return newAnimeLoadResponse(title, url, TvType.NSFW) { engName = title @@ -137,7 +117,6 @@ class Minioppai : MainAPI() { showStatus = status plot = description this.tags = tags - posterHeaders = cloudflareKiller.getCookieHeaders(mainUrl).toMap() } } @@ -147,7 +126,7 @@ class Minioppai : MainAPI() { subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit ): Boolean { - val document = app.get(data, interceptor = interceptor).document + val document = app.get(data).document document.select("div.server ul.mirror li a").mapNotNull { Jsoup.parse(base64Decode(it.attr("data-em"))).select("iframe").attr("src") }.apmap { link -> diff --git a/Moviehab/build.gradle.kts b/Moviehab/build.gradle.kts index ee715c13..29b7c042 100644 --- a/Moviehab/build.gradle.kts +++ b/Moviehab/build.gradle.kts @@ -1,5 +1,5 @@ // use an integer for version numbers -version = 2 +version = 3 cloudstream { @@ -23,4 +23,4 @@ cloudstream { ) iconUrl = "https://www.google.com/s2/favicons?domain=moviehab.com&sz=%size%" -} \ No newline at end of file +} diff --git a/Moviehab/src/main/kotlin/com/hexated/Moviehab.kt b/Moviehab/src/main/kotlin/com/hexated/Moviehab.kt index 6a34788f..21f27395 100644 --- a/Moviehab/src/main/kotlin/com/hexated/Moviehab.kt +++ b/Moviehab/src/main/kotlin/com/hexated/Moviehab.kt @@ -12,7 +12,7 @@ import com.lagradost.cloudstream3.utils.loadExtractor import org.jsoup.nodes.Element class Moviehab : MainAPI() { - override var mainUrl = "https://vivamax.asia" + override var mainUrl = "https://nowshowing.to" override var name = "Moviehab" override val hasMainPage = true override var lang = "tl" @@ -23,16 +23,13 @@ class Moviehab : MainAPI() { ) companion object { - private const val mainServer = "https://vivamax.asia" + private const val mainServer = "https://nowshowing.to" } override val mainPage = mainPageOf( - "$mainUrl/library/movies?sort_by=imdb_rate&page=" to "Movies by IMDB Rating", - "$mainUrl/library/shows?&sort_by=imdb_rate&page=" to "TV Shows by IMDB Rating", - "$mainUrl/library/movies?&sort_by=year&page=" to "New Movies", - "$mainUrl/library/shows?&sort_by=year&page=" to "New TV Shows", - "$mainUrl/library/movies?country=Philippines&sort_by=year&page=" to "New Philippines Movies", - "$mainUrl/library/shows?&country=Philippines&sort_by=year&page=" to "New Philippines TV Shows", + "$mainUrl/category/movies/" to "New Movies", + "$mainUrl/category/tv-series/" to "New TV Shows", + "$mainUrl/category/coming-soon/" to "Coming Soon", ) override suspend fun getMainPage( diff --git a/Movierulzhd/build.gradle.kts b/Movierulzhd/build.gradle.kts index 5d09bb3d..08c2a426 100644 --- a/Movierulzhd/build.gradle.kts +++ b/Movierulzhd/build.gradle.kts @@ -1,12 +1,12 @@ // use an integer for version numbers -version = 53 +version = 55 cloudstream { language = "hi" // All of these properties are optional, you can safely remove them - description = "Include: Hdmovie2" + description = "Includes: Hdmovie2, Animesaga" authors = listOf("Hexated") /** diff --git a/Movierulzhd/src/main/kotlin/com/hexated/Animesaga.kt b/Movierulzhd/src/main/kotlin/com/hexated/Animesaga.kt new file mode 100644 index 00000000..524ac176 --- /dev/null +++ b/Movierulzhd/src/main/kotlin/com/hexated/Animesaga.kt @@ -0,0 +1,23 @@ +package com.hexated + +import com.lagradost.cloudstream3.TvType +import com.lagradost.cloudstream3.mainPageOf + +class Animesaga : Movierulzhd() { + + override var mainUrl = "https://www.animesaga.in" + override var name = "Animesaga" + override val supportedTypes = setOf( + TvType.Anime, + TvType.AnimeMovie, + TvType.OVA + ) + + override val mainPage = mainPageOf( + "movies" to "Movies", + "tvshows" to "TV-Shows", + "genre/hindi-dub" to "Hindi Dub", + ) + + +} \ No newline at end of file diff --git a/Movierulzhd/src/main/kotlin/com/hexated/Extractors.kt b/Movierulzhd/src/main/kotlin/com/hexated/Extractors.kt index 37772c96..c3a84b83 100644 --- a/Movierulzhd/src/main/kotlin/com/hexated/Extractors.kt +++ b/Movierulzhd/src/main/kotlin/com/hexated/Extractors.kt @@ -2,10 +2,9 @@ package com.hexated import com.lagradost.cloudstream3.SubtitleFile import com.lagradost.cloudstream3.app +import com.lagradost.cloudstream3.extractors.Chillx import com.lagradost.cloudstream3.utils.* -const val twoEmbedAPI = "https://www.2embed.to" - class Sbnmp : ExtractorApi() { override val name = "Sbnmp" override var mainUrl = "https://sbnmp.bar" @@ -66,3 +65,8 @@ open class Akamaicdn : ExtractorApi() { ) } } + +class AnimesagaStream : Chillx() { + override val name = "AnimesagaStream" + override val mainUrl = "https://stream.animesaga.in" +} diff --git a/Movierulzhd/src/main/kotlin/com/hexated/Hdmovie2.kt b/Movierulzhd/src/main/kotlin/com/hexated/Hdmovie2.kt index fafaebdf..5af01bf1 100644 --- a/Movierulzhd/src/main/kotlin/com/hexated/Hdmovie2.kt +++ b/Movierulzhd/src/main/kotlin/com/hexated/Hdmovie2.kt @@ -12,7 +12,7 @@ import org.jsoup.Jsoup class Hdmovie2 : Movierulzhd() { - override var mainUrl = "https://hdmovie2.boo" + override var mainUrl = "https://hdmovie2.li" override var name = "Hdmovie2" override val mainPage = mainPageOf( "trending" to "Trending", @@ -44,7 +44,7 @@ class Hdmovie2 : Movierulzhd() { } else { var document = app.get(data).document if (document.select("title").text() == "Just a moment...") { - document = app.get(data, interceptor = interceptor).document + document = app.get(data).document } val id = document.select("meta#dooplay-ajax-counter").attr("data-postid") val type = if (data.contains("/movies/")) "movie" else "tv" diff --git a/Movierulzhd/src/main/kotlin/com/hexated/Movierulzhd.kt b/Movierulzhd/src/main/kotlin/com/hexated/Movierulzhd.kt index b66bf0a2..77260037 100644 --- a/Movierulzhd/src/main/kotlin/com/hexated/Movierulzhd.kt +++ b/Movierulzhd/src/main/kotlin/com/hexated/Movierulzhd.kt @@ -4,7 +4,6 @@ import com.fasterxml.jackson.annotation.JsonProperty import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.LoadResponse.Companion.addActors import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer -import com.lagradost.cloudstream3.network.CloudflareKiller import com.lagradost.cloudstream3.utils.* import com.lagradost.cloudstream3.utils.AppUtils.toJson import org.jsoup.nodes.Element @@ -34,18 +33,13 @@ open class Movierulzhd : MainAPI() { "episodes" to "Episode", ) - val interceptor = CloudflareKiller() - override suspend fun getMainPage( page: Int, request: MainPageRequest ): HomePageResponse { - var document = app.get("$mainUrl/${request.data}/page/$page").document - if (document.select("title").text() == "Just a moment...") { - document = app.get(request.data + page, interceptor = interceptor).document - } + val document = app.get("$mainUrl/${request.data}/page/$page").document val home = - document.select("div.items.normal article, div#archive-content article").mapNotNull { + document.select("div.items.normal article, div#archive-content article, div.items.full article").mapNotNull { it.toSearchResult() } return newHomePageResponse(request.name, home) @@ -74,23 +68,17 @@ open class Movierulzhd : MainAPI() { private fun Element.toSearchResult(): SearchResponse? { val title = this.selectFirst("h3 > a")?.text() ?: return null val href = getProperLink(fixUrl(this.selectFirst("h3 > a")!!.attr("href"))) - val posterUrl = fixUrlNull(this.select("div.poster img").last()?.attr("src")) + val posterUrl = fixUrlNull(this.select("div.poster img").last()?.getImageAttr()) val quality = getQualityFromString(this.select("span.quality").text()) return newMovieSearchResponse(title, href, TvType.Movie) { this.posterUrl = posterUrl this.quality = quality - posterHeaders = interceptor.getCookieHeaders(mainUrl).toMap() } } override suspend fun search(query: String): List { - val link = "$mainUrl/search/$query" - var document = app.get(link).document - if (document.select("title").text() == "Just a moment...") { - document = app.get(link, interceptor = interceptor).document - } - + val document = app.get("$mainUrl/search/$query").document return document.select("div.result-item").map { val title = it.selectFirst("div.title > a")!!.text().replace(Regex("\\(\\d{4}\\)"), "").trim() @@ -98,21 +86,17 @@ open class Movierulzhd : MainAPI() { val posterUrl = it.selectFirst("img")!!.attr("src").toString() newMovieSearchResponse(title, href, TvType.TvSeries) { this.posterUrl = posterUrl - posterHeaders = interceptor.getCookieHeaders(mainUrl).toMap() } } } override suspend fun load(url: String): LoadResponse { val request = app.get(url) - var document = request.document - if (document.select("title").text() == "Just a moment...") { - document = app.get(url, interceptor = interceptor).document - } + val document = request.document directUrl = getBaseUrl(request.url) val title = document.selectFirst("div.data > h1")?.text()?.trim().toString() - val poster = fixUrlNull(document.select("div.poster img:last-child").attr("src")) + val poster = fixUrlNull(document.selectFirst("div.poster img:last-child")?.getImageAttr()) val tags = document.select("div.sgeneros > a").map { it.text() } val year = Regex(",\\s?(\\d+)").find( @@ -139,10 +123,9 @@ open class Movierulzhd : MainAPI() { val recName = it.selectFirst("a")!!.attr("href").toString().removeSuffix("/").split("/").last() val recHref = it.selectFirst("a")!!.attr("href") - val recPosterUrl = it.selectFirst("img")?.attr("src").toString() + val recPosterUrl = it.selectFirst("img")?.getImageAttr() newTvSeriesSearchResponse(recName, recHref, TvType.TvSeries) { this.posterUrl = recPosterUrl - posterHeaders = interceptor.getCookieHeaders(url).toMap() } } @@ -151,7 +134,7 @@ open class Movierulzhd : MainAPI() { document.select("ul.episodios > li").map { val href = it.select("a").attr("href") val name = fixTitle(it.select("div.episodiotitle > a").text().trim()) - val image = it.select("div.imagen > img").attr("src") + val image = it.selectFirst("div.imagen > img")?.getImageAttr() val episode = it.select("div.numerando").text().replace(" ", "").split("-").last() .toIntOrNull() @@ -187,7 +170,6 @@ open class Movierulzhd : MainAPI() { addActors(actors) this.recommendations = recommendations addTrailer(trailer) - posterHeaders = interceptor.getCookieHeaders(url).toMap() } } else { newMovieLoadResponse(title, url, TvType.Movie, url) { @@ -199,7 +181,6 @@ open class Movierulzhd : MainAPI() { addActors(actors) this.recommendations = recommendations addTrailer(trailer) - posterHeaders = interceptor.getCookieHeaders(url).toMap() } } } @@ -230,18 +211,17 @@ open class Movierulzhd : MainAPI() { referer = data, headers = mapOf("X-Requested-With" to "XMLHttpRequest") ).parsed().embed_url - if (!source.contains("youtube")) loadCustomExtractor(loadData?.tag, source, "$directUrl/", subtitleCallback, callback) + if (!source.contains("youtube")) loadCustomExtractor(source, "$directUrl/", subtitleCallback, callback) } else { - var document = app.get(data).document - if (document.select("title").text() == "Just a moment...") { - document = app.get(data, interceptor = interceptor).document - } - val id = document.select("meta#dooplay-ajax-counter").attr("data-postid") - val type = if (data.contains("/movies/")) "movie" else "tv" + val document = app.get(data).document document.select("ul#playeroptionsul > li").map { - it.attr("data-nume") to it.select("span.title").text() - }.apmap { (nume, tag) -> + Triple( + it.attr("data-post"), + it.attr("data-nume"), + it.attr("data-type") + ) + }.apmap { (id, nume, type) -> val source = app.post( url = "$directUrl/wp-admin/admin-ajax.php", data = mapOf( @@ -256,7 +236,6 @@ open class Movierulzhd : MainAPI() { when { !source.contains("youtube") -> loadCustomExtractor( - tag, source, "$directUrl/", subtitleCallback, @@ -269,8 +248,16 @@ open class Movierulzhd : MainAPI() { return true } + private fun Element.getImageAttr(): String? { + return when { + this.hasAttr("data-src") -> this.attr("abs:data-src") + this.hasAttr("data-lazy-src") -> this.attr("abs:data-lazy-src") + this.hasAttr("srcset") -> this.attr("abs:srcset").substringBefore(" ") + else -> this.attr("abs:src") + } + } + private suspend fun loadCustomExtractor( - name: String? = null, url: String, referer: String? = null, subtitleCallback: (SubtitleFile) -> Unit, @@ -278,21 +265,23 @@ open class Movierulzhd : MainAPI() { quality: Int? = null, ) { loadExtractor(url, referer, subtitleCallback) { link -> - callback.invoke( - ExtractorLink( - name ?: link.source, - name ?: link.name, - link.url, - link.referer, - when (link.type) { - ExtractorLinkType.M3U8 -> link.quality - else -> quality ?: link.quality - }, - link.type, - link.headers, - link.extractorData + if(link.quality == Qualities.Unknown.value) { + callback.invoke( + ExtractorLink( + link.source, + link.name, + link.url, + link.referer, + when (link.type) { + ExtractorLinkType.M3U8 -> link.quality + else -> quality ?: link.quality + }, + link.type, + link.headers, + link.extractorData + ) ) - ) + } } } diff --git a/Movierulzhd/src/main/kotlin/com/hexated/MovierulzhdPlugin.kt b/Movierulzhd/src/main/kotlin/com/hexated/MovierulzhdPlugin.kt index 9ef951a8..680b49a0 100644 --- a/Movierulzhd/src/main/kotlin/com/hexated/MovierulzhdPlugin.kt +++ b/Movierulzhd/src/main/kotlin/com/hexated/MovierulzhdPlugin.kt @@ -11,7 +11,9 @@ class MovierulzhdPlugin: Plugin() { // All providers should be added in this manner. Please don't edit the providers list directly. registerMainAPI(Movierulzhd()) registerMainAPI(Hdmovie2()) + registerMainAPI(Animesaga()) registerExtractorAPI(Sbnmp()) registerExtractorAPI(Akamaicdn()) + registerExtractorAPI(AnimesagaStream()) } } \ No newline at end of file diff --git a/Samehadaku/src/main/kotlin/com/hexated/Samehadaku.kt b/Samehadaku/src/main/kotlin/com/hexated/Samehadaku.kt index eb5f1dd8..51404e62 100644 --- a/Samehadaku/src/main/kotlin/com/hexated/Samehadaku.kt +++ b/Samehadaku/src/main/kotlin/com/hexated/Samehadaku.kt @@ -4,13 +4,9 @@ import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.LoadResponse.Companion.addAniListId import com.lagradost.cloudstream3.LoadResponse.Companion.addMalId import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer -import com.lagradost.cloudstream3.network.CloudflareKiller import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.Qualities import com.lagradost.cloudstream3.utils.loadExtractor -import okhttp3.Interceptor -import okhttp3.Response -import org.jsoup.Jsoup import org.jsoup.nodes.Element class Samehadaku : MainAPI() { @@ -19,26 +15,12 @@ class Samehadaku : MainAPI() { override val hasMainPage = true override var lang = "id" override val hasDownloadSupport = true - private val cloudflareKiller by lazy { CloudflareKiller() } - private val interceptor by lazy { CloudflareInterceptor(cloudflareKiller) } override val supportedTypes = setOf( TvType.Anime, TvType.AnimeMovie, TvType.OVA ) - class CloudflareInterceptor(private val cloudflareKiller: CloudflareKiller): Interceptor { - override fun intercept(chain: Interceptor.Chain): Response { - val request = chain.request() - val response = chain.proceed(request) - val doc = Jsoup.parse(response.peekBody(1024 * 1024).string()) - if (doc.select("title").text() == "Just a moment...") { - return cloudflareKiller.intercept(chain) - } - return response - } - } - companion object { const val acefile = "https://acefile.co" @@ -69,7 +51,7 @@ class Samehadaku : MainAPI() { val items = mutableListOf() if (request.name != "Episode Terbaru" && page <= 1) { - val doc = app.get(request.data, interceptor = interceptor).document + val doc = app.get(request.data).document doc.select("div.widget_senction:not(:contains(Baca Komik))").forEach { block -> val header = block.selectFirst("div.widget-title h3")?.ownText() ?: return@forEach val home = block.select("div.animepost").mapNotNull { @@ -80,8 +62,7 @@ class Samehadaku : MainAPI() { } if (request.name == "Episode Terbaru") { - val home = - app.get(request.data + page, interceptor = interceptor).document.selectFirst("div.post-show")?.select("ul li") + val home = app.get(request.data + page).document.selectFirst("div.post-show")?.select("ul li") ?.mapNotNull { it.toSearchResult() } ?: throw ErrorLoadingException("No Media Found") @@ -101,13 +82,12 @@ class Samehadaku : MainAPI() { return newAnimeSearchResponse(title, href ?: return null, TvType.Anime) { this.posterUrl = posterUrl addSub(epNum) - posterHeaders = cloudflareKiller.getCookieHeaders(mainUrl).toMap() } } override suspend fun search(query: String): List { - val document = app.get("$mainUrl/?s=$query", interceptor = interceptor).document + val document = app.get("$mainUrl/?s=$query").document return document.select("main#main div.animepost").mapNotNull { it.toSearchResult() } @@ -117,10 +97,10 @@ class Samehadaku : MainAPI() { val fixUrl = if (url.contains("/anime/")) { url } else { - app.get(url, interceptor = interceptor).document.selectFirst("div.nvs.nvsc a")?.attr("href") + app.get(url).document.selectFirst("div.nvs.nvsc a")?.attr("href") } - val document = app.get(fixUrl ?: return null, interceptor = interceptor).document + val document = app.get(fixUrl ?: return null).document val title = document.selectFirst("h1.entry-title")?.text()?.removeBloat() ?: return null val poster = document.selectFirst("div.thumb > img")?.attr("src") val tags = document.select("div.genre-info > a").map { it.text() } @@ -165,7 +145,6 @@ class Samehadaku : MainAPI() { this.recommendations = recommendations addMalId(tracker?.malId) addAniListId(tracker?.aniId?.toIntOrNull()) - posterHeaders = cloudflareKiller.getCookieHeaders(mainUrl).toMap() } } @@ -177,7 +156,7 @@ class Samehadaku : MainAPI() { callback: (ExtractorLink) -> Unit ): Boolean { - val document = app.get(data, interceptor = interceptor).document + val document = app.get(data).document argamap( { @@ -196,7 +175,6 @@ class Samehadaku : MainAPI() { ), referer = data, headers = mapOf("X-Requested-With" to "XMLHttpRequest"), - interceptor = interceptor ).document.select("iframe").attr("src") loadFixedExtractor(fixedIframe(iframe), it.text(), "$mainUrl/", subtitleCallback, callback) diff --git a/SoraStream/build.gradle.kts b/SoraStream/build.gradle.kts index 6824105e..c924f475 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 = 197 +version = 204 android { defaultConfig { @@ -9,15 +9,12 @@ android { properties.load(project.rootProject.file("local.properties").inputStream()) buildConfigField("String", "TMDB_API", "\"${properties.getProperty("TMDB_API")}\"") + buildConfigField("String", "OMOVIES_API", "\"${properties.getProperty("OMOVIES_API")}\"") + buildConfigField("String", "CINEMATV_API", "\"${properties.getProperty("CINEMATV_API")}\"") buildConfigField("String", "SFMOVIES_API", "\"${properties.getProperty("SFMOVIES_API")}\"") buildConfigField("String", "ZSHOW_API", "\"${properties.getProperty("ZSHOW_API")}\"") - buildConfigField("String", "SORA_API", "\"${properties.getProperty("SORA_API")}\"") - buildConfigField("String", "SORAHE", "\"${properties.getProperty("SORAHE")}\"") - buildConfigField("String", "SORAXA", "\"${properties.getProperty("SORAXA")}\"") - buildConfigField("String", "SORATED", "\"${properties.getProperty("SORATED")}\"") buildConfigField("String", "DUMP_API", "\"${properties.getProperty("DUMP_API")}\"") buildConfigField("String", "DUMP_KEY", "\"${properties.getProperty("DUMP_KEY")}\"") - buildConfigField("String", "PRIMEWIRE_KEY", "\"${properties.getProperty("PRIMEWIRE_KEY")}\"") buildConfigField("String", "CRUNCHYROLL_BASIC_TOKEN", "\"${properties.getProperty("CRUNCHYROLL_BASIC_TOKEN")}\"") buildConfigField("String", "CRUNCHYROLL_REFRESH_TOKEN", "\"${properties.getProperty("CRUNCHYROLL_REFRESH_TOKEN")}\"") } diff --git a/SoraStream/src/main/kotlin/com/hexated/Extractors.kt b/SoraStream/src/main/kotlin/com/hexated/Extractors.kt index 917351ca..5fb910af 100644 --- a/SoraStream/src/main/kotlin/com/hexated/Extractors.kt +++ b/SoraStream/src/main/kotlin/com/hexated/Extractors.kt @@ -6,17 +6,13 @@ import com.lagradost.cloudstream3.extractors.StreamSB import com.lagradost.cloudstream3.extractors.Voe import com.fasterxml.jackson.annotation.JsonProperty import com.lagradost.cloudstream3.APIHolder.getCaptchaToken -import com.lagradost.cloudstream3.ErrorLoadingException import com.lagradost.cloudstream3.SubtitleFile import com.lagradost.cloudstream3.apmap import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.base64Decode import com.lagradost.cloudstream3.extractors.Pixeldrain -import com.lagradost.cloudstream3.extractors.helper.AesHelper +import com.lagradost.cloudstream3.extractors.Vidplay import com.lagradost.cloudstream3.utils.* -import org.jsoup.nodes.Document -import org.mozilla.javascript.Context -import org.mozilla.javascript.Scriptable import java.math.BigInteger import java.security.MessageDigest @@ -202,9 +198,10 @@ open class VCloud : ExtractorApi() { val res = app.get(url) val doc = res.document val changedLink = doc.selectFirst("script:containsData(url =)")?.data()?.let { - """url\s*=\s*['"](.*)['"];""".toRegex().find(it)?.groupValues?.get(1) - ?.substringAfter("r=") - } ?: doc.selectFirst("div.div.vd.d-none a")?.attr("href") + val regex = """url\s*=\s*['"](.*)['"];""".toRegex() + val doc2 = app.get(regex.find(it)?.groupValues?.get(1) ?: return).text + regex.find(doc2)?.groupValues?.get(1)?.substringAfter("r=") + } val header = doc.selectFirst("div.card-header")?.text() app.get( base64Decode(changedLink ?: return), cookies = res.cookies, headers = mapOf( @@ -212,7 +209,7 @@ open class VCloud : ExtractorApi() { ) ).document.select("p.text-success ~ a").apmap { val link = it.attr("href") - if (link.contains("workers.dev")) { + if (link.contains("workers.dev") || it.text().contains("[Server : 1]") || link.contains("/dl.php?")) { callback.invoke( ExtractorLink( this.name, @@ -238,83 +235,6 @@ open class VCloud : ExtractorApi() { } -object NineTv { - - suspend fun getUrl( - url: String, - referer: String?, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ) { - val mainUrl = getBaseUrl(url) - val res = app.get(url, headers = mapOf( - "Accept" to "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8", - "Accept-Language" to "en-US,en;q=0.5", - "Referer" to (referer ?: ""), - )) - val master = Regex("\\s*=\\s*'([^']+)").find(res.text)?.groupValues?.get(1) - val key = "tSIsE8FgpRkv3QQQ" - val decrypt = AesHelper.cryptoAESHandler(master ?: return, key.toByteArray(), false) - ?.replace("\\", "") - ?: throw ErrorLoadingException("failed to decrypt") - - val source = Regex(""""?file"?:\s*"([^"]+)""").find(decrypt)?.groupValues?.get(1) - val tracks = Regex("""tracks:\s*\[(.+)]""").find(decrypt)?.groupValues?.get(1) - val name = url.getHost() - - M3u8Helper.generateM3u8( - name, - source ?: return, - "$mainUrl/", - headers = mapOf( - "Accept" to "*/*", - "Connection" to "keep-alive", - "Sec-Fetch-Dest" to "empty", - "Sec-Fetch-Mode" to "cors", - "Sec-Fetch-Site" to "cross-site", - "Origin" to mainUrl, - ) - ).forEach(callback) - - AppUtils.tryParseJson>("[$tracks]") - ?.filter { it.kind == "captions" }?.map { track -> - subtitleCallback.invoke( - SubtitleFile( - track.label ?: "", - track.file ?: return@map null - ) - ) - } - } - - private fun Document.getKeys(): String? { - val script = (this.selectFirst("script:containsData(eval\\()")?.data() - ?.replace("eval(", "var result=")?.removeSuffix(");") + ";").trimIndent() - val run = script.runJS("result") - return """,\s*'([^']+)""".toRegex().find(run)?.groupValues?.getOrNull(1) - } - - private fun String.runJS(variable: String): String { - val rhino = Context.enter() - rhino.optimizationLevel = -1 - val scope: Scriptable = rhino.initSafeStandardObjects() - val result: String - try { - rhino.evaluateString(scope, this, "JavaScript", 1, null) - result = Context.toString(scope.get(variable, scope)) - } finally { - Context.exit() - } - return result - } - - data class Tracks( - @JsonProperty("file") val file: String? = null, - @JsonProperty("label") val label: String? = null, - @JsonProperty("kind") val kind: String? = null, - ) -} - open class Streamruby : ExtractorApi() { override val name = "Streamruby" override val mainUrl = "https://streamruby.com" @@ -390,6 +310,40 @@ open class Uploadever : ExtractorApi() { } +open class Netembed : ExtractorApi() { + override var name: String = "Netembed" + override var mainUrl: String = "https://play.netembed.xyz" + override val requiresReferer = true + + override suspend fun getUrl( + url: String, + referer: String?, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ) { + val response = app.get(url, referer = referer) + val script = getAndUnpack(response.text) + val m3u8 = Regex("((https:|http:)//.*\\.m3u8)").find(script)?.groupValues?.getOrNull(1) + + callback.invoke( + ExtractorLink( + this.name, + this.name, + m3u8 ?: return, + "$mainUrl/", + getQuality(m3u8), + INFER_TYPE + ) + ) + } + + private suspend fun getQuality(url: String) : Int { + val res = app.get(url, referer = "$mainUrl/").text + val regex = "#quality:\\s*(\\S+)".toRegex().find(res)?.groupValues?.get(1) + return getQualityFromName(regex) + } +} + class Streamwish : Filesim() { override val name = "Streamwish" override var mainUrl = "https://streamwish.to" @@ -405,11 +359,6 @@ class FilelionsTo : Filesim() { override var mainUrl = "https://filelions.to" } -class Hubcloud : VCloud() { - override val name = "Hubcloud" - override val mainUrl = "https://hubcloud.in" -} - class Pixeldra : Pixeldrain() { override val mainUrl = "https://pixeldra.in" } @@ -447,4 +396,8 @@ class Yipsu : Voe() { class Embedwish : Filesim() { override val name = "Embedwish" override var mainUrl = "https://embedwish.com" +} + +class Vidplay2 : Vidplay() { + override val mainUrl = "https://vidplay.online" } \ No newline at end of file diff --git a/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt b/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt index 261b0e18..c14b6ec4 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt @@ -2,6 +2,7 @@ package com.hexated import com.hexated.AESGCM.decrypt import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.APIHolder.unixTime import com.lagradost.cloudstream3.APIHolder.unixTimeMS import com.lagradost.cloudstream3.utils.* import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson @@ -110,7 +111,8 @@ object SoraExtractor : SoraStream() { "$vidSrcAPI/embed/tv?tmdb=$id&season=$season&episode=$episode" } - val iframedoc = app.get(url).document.select("iframe#player_iframe").attr("src").let { httpsify(it) } + val iframedoc = + app.get(url).document.select("iframe#player_iframe").attr("src").let { httpsify(it) } val doc = app.get(iframedoc, referer = url).document val index = doc.select("body").attr("data-i") @@ -246,6 +248,7 @@ object SoraExtractor : SoraStream() { } else { "$multimoviesAPI/episodes/$fixTitle-${season}x${episode}" } + invokeWpmovies(null, url, subtitleCallback, callback) } suspend fun invokeNetmovies( @@ -273,12 +276,11 @@ object SoraExtractor : SoraStream() { subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit ) { - val api = BuildConfig.ZSHOW_API val fixTitle = title.createSlug() val url = if (season == null) { - "$api/movie/$fixTitle-$year" + "$zshowAPI/movie/$fixTitle-$year" } else { - "$api/episode/$fixTitle-season-$season-episode-$episode" + "$zshowAPI/episode/$fixTitle-season-$season-episode-$episode" } invokeWpmovies("ZShow", url, subtitleCallback, callback, encrypt = true) } @@ -326,20 +328,7 @@ object SoraExtractor : SoraStream() { else -> it.embed_url } } ?: return@apmap - val sources = arrayOf( - "https://chillx.top", - "https://watchx.top", - "https://bestx.stream", - "https://w1.moviesapi.club" - ) when { - sources.any { source.startsWith(it) } -> NineTv.getUrl( - source, - "$referer/", - subtitleCallback, - callback - ) - !source.contains("youtube") -> { loadCustomExtractor(name, source, "$referer/", subtitleCallback, callback) } @@ -451,7 +440,7 @@ object SoraExtractor : SoraStream() { sourcesData?.get("movie")?.get("movie") } else { sourcesData?.get("s$seasonSlug")?.get("e$episodeSlug") - } + } ?: return val subSources = if (season == null) { subSourcesData?.get("movie")?.get("movie") } else { @@ -464,39 +453,45 @@ object SoraExtractor : SoraStream() { 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 json = app.post( - "$filmxyAPI/wp-admin/admin-ajax.php", - requestBody = "action=get_vid_links$linkIDs&user_id=$userId&nonce=$userNonce".toRequestBody(), - 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 = filmxyCookies - ).text.let { tryParseJson>(it) } + val listSources = sources.withIndex() + .groupBy { it.index / 2 } + .map { entry -> entry.value.map { it.value } } - sources?.map { source -> - val link = json?.get(source) - val quality = sourcesDetail?.get(source)?.get("resolution") - val server = sourcesDetail?.get(source)?.get("server") - val size = sourcesDetail?.get(source)?.get("size") + listSources.apmap { src -> + val linkIDs = src.joinToString("") { + "&linkIDs%5B%5D=$it" + }.replace("\"", "") + val json = app.post( + "$filmxyAPI/wp-admin/admin-ajax.php", + requestBody = "action=get_vid_links$linkIDs&user_id=$userId&nonce=$userNonce".toRequestBody(), + 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 = filmxyCookies + ).text.let { tryParseJson>(it) } - callback.invoke( - ExtractorLink( - "Filmxy", - "Filmxy $server [$size]", - link ?: return@map, - "$filmxyAPI/", - getQualityFromName(quality) + src.map { source -> + val link = json?.get(source) + val quality = sourcesDetail?.get(source)?.get("resolution") + val server = sourcesDetail?.get(source)?.get("server") + val size = sourcesDetail?.get(source)?.get("size") + + callback.invoke( + ExtractorLink( + "Filmxy", + "Filmxy $server [$size]", + link ?: return@map, + "$filmxyAPI/", + getQualityFromName(quality) + ) ) - ) + } } subSources?.mapKeys { sub -> @@ -686,12 +681,20 @@ object SoraExtractor : SoraStream() { "$vidsrctoAPI/embed/tv/$imdbId/$season/$episode" } - val mediaId = app.get(url).document.selectFirst("ul.episodes li a")?.attr("data-id") ?: return + val mediaId = + app.get(url).document.selectFirst("ul.episodes li a")?.attr("data-id") ?: return - app.get("$vidsrctoAPI/ajax/embed/episode/$mediaId/sources").parsedSafe()?.result?.apmap { - val encUrl = app.get("$vidsrctoAPI/ajax/embed/source/${it.id}").parsedSafe()?.result?.url - loadExtractor(vidsrctoDecrypt(encUrl ?: return@apmap), "$vidsrctoAPI/", subtitleCallback, callback) - } + app.get("$vidsrctoAPI/ajax/embed/episode/$mediaId/sources") + .parsedSafe()?.result?.apmap { + val encUrl = app.get("$vidsrctoAPI/ajax/embed/source/${it.id}") + .parsedSafe()?.result?.url + loadExtractor( + vidsrctoDecrypt(encUrl ?: return@apmap), + "$vidsrctoAPI/", + subtitleCallback, + callback + ) + } val subtitles = app.get("$vidsrctoAPI/ajax/embed/episode/$mediaId/subtitles").text tryParseJson>(subtitles)?.map { @@ -965,62 +968,50 @@ object SoraExtractor : SoraStream() { title: String? = null, year: Int? = null, season: Int? = null, - lastSeason: Int? = null, episode: Int? = null, callback: (ExtractorLink) -> Unit ) { - val slug = title.createSlug()?.replace("-", " ") - val url = "$uhdmoviesAPI/?s=$slug" - var doc = app.get(url).document - if (doc.select("title").text() == "Just a moment...") { - doc = app.get(url, interceptor = CloudflareKiller()).document - } - val scriptData = doc.select("div.row.gridlove-posts article").map { - it.selectFirst("a")?.attr("href") to it.selectFirst("h1")?.text() - } + val fixTitle = title.createSlug() + val (seasonSlug, episodeSlug) = getEpisodeSlug(season, episode) - val detailUrl = (if (scriptData.size == 1) { - scriptData.first() + val url = if(season == null) { + "$uhdmoviesAPI/download-$fixTitle-$year" } else { - scriptData.find { it.second?.filterMedia(title, year, lastSeason) == true } - })?.first + "$uhdmoviesAPI/download-$fixTitle" + } - val detailDoc = app.get(detailUrl ?: return).document + val detailDoc = app.get(url).document - val iframeList = detailDoc.select("div.entry-content p").map { it } - .filter { it.text().filterIframe(season, lastSeason, year, title) }.mapNotNull { - if (season == null) { - it.text() to it.nextElementSibling()?.select("a")?.attr("href") - } else { - it.text() to it.nextElementSibling()?.select("a")?.find { child -> - child.select("span").text().equals("Episode $episode", true) - }?.attr("href") - } - }.filter { it.second?.contains(Regex("(https:)|(http:)")) == true } + val iSelector = if(season == null) { + "div.entry-content p:has(:matches($year))" + } else { + "div.entry-content p:has(:matches((?i)(?:S\\s*$seasonSlug|Season\\s*$seasonSlug)))" + } + val iframeList = detailDoc.select(iSelector).mapNotNull { + if (season == null) { + it.text() to it.nextElementSibling()?.select("a")?.attr("href") + } else { + it.text() to it.nextElementSibling()?.select("a")?.find { child -> + child.select("span").text().equals("Episode $episode", true) + }?.attr("href") + } + }.filter { it.first.contains(Regex("(2160p)|(1080p)")) } iframeList.apmap { (quality, link) -> - val driveLink = - when { - link?.contains("oddfirm") == true -> bypassHrefli(link) - link?.contains("driveleech") == true -> bypassDriveleech(link) - else -> bypassTechmny(link ?: return@apmap) - } + val driveLink = bypassHrefli(link ?: return@apmap) val base = getBaseUrl(driveLink ?: return@apmap) val driveReq = app.get(driveLink) val driveRes = driveReq.document val bitLink = driveRes.select("a.btn.btn-outline-success").attr("href") - val insLink = - driveRes.select("a.btn.btn-danger:contains(Instant Download)").attr("href") + val insLink = driveRes.select("a.btn.btn-danger:contains(Instant Download)").attr("href") val downloadLink = when { insLink.isNotEmpty() -> extractInstantUHD(insLink) driveRes.select("button.btn.btn-success").text() .contains("Direct Download", true) -> extractDirectUHD(driveLink, driveReq) - bitLink.isNullOrEmpty() -> { val backupIframe = driveRes.select("a.btn.btn-outline-warning").attr("href") extractBackupUHD(backupIframe ?: return@apmap) } - else -> { extractMirrorUHD(bitLink, base) } @@ -1038,10 +1029,7 @@ object SoraExtractor : SoraStream() { qualities ) ) - } - - } suspend fun invokeDotmovies( @@ -1103,40 +1091,34 @@ object SoraExtractor : SoraStream() { 1 -> "Season 1" else -> "Season 1 – $lastSeason" } - val media = - res.selectFirst("div.blog-items article:has(h3.entry-title:matches((?i)$title.*$match)) a") + val media = res.selectFirst("div.blog-items article:has(h3.entry-title:matches((?i)$title.*$match)) a") ?.attr("href") res = app.get(media ?: return).document val hTag = if (season == null) "h5" else "h3" val aTag = if (season == null) "Download Now" else "V-Cloud" val sTag = if (season == null) "" else "(Season $season|S$seasonSlug)" - res.select("div.entry-content > $hTag:matches((?i)$sTag.*(1080p|2160p))") - .filter { element -> !element.text().contains("Download", true) }.apmap { - val tags = - """(?:1080p|2160p)(.*)""".toRegex().find(it.text())?.groupValues?.get(1)?.trim() - val href = - it.nextElementSibling()?.select("a:contains($aTag)")?.attr("href")?.let { url -> - app.post( - "${getBaseUrl(url)}/red.php", - data = mapOf("link" to url), - referer = "$api/" - ).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") - loadCustomTagExtractor( - tags, - server ?: return@apmap, - "$api/", - subtitleCallback, - callback, - getIndexQuality(it.text()) - ) - } + val entry = res.select("div.entry-content > $hTag:matches((?i)$sTag.*(1080p|2160p))") + .findLast { element -> !element.text().contains("Download", true) } ?: return + val tags = + """(?:1080p|2160p)(.*)""".toRegex().find(entry.text())?.groupValues?.get(1)?.trim() + val href = + entry.nextElementSibling()?.select("a:contains($aTag)")?.attr("href") + 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, interceptor = CloudflareKiller() + ).document.selectFirst("div.entry-content > $selector") + ?.attr("href") ?: return + + loadCustomTagExtractor( + tags, + server, + "$api/", + subtitleCallback, + callback, + getIndexQuality(entry.text()) + ) } suspend fun invokeHdmovies4u( @@ -1213,7 +1195,14 @@ object SoraExtractor : SoraStream() { iframe.apmap { (iframeLink, title) -> val size = Regex("(?i)\\s(\\S+gb|mb)").find(title)?.groupValues?.getOrNull(1) - loadCustomTagExtractor("[$size]",iframeLink, "$gMoviesAPI/", subtitleCallback, callback, getIndexQuality(title)) + loadCustomTagExtractor( + "[$size]", + iframeLink, + "$gMoviesAPI/", + subtitleCallback, + callback, + getIndexQuality(title) + ) } } @@ -1304,7 +1293,9 @@ object SoraExtractor : SoraStream() { scriptData.firstOrNull() } else { scriptData.find { - it.first.contains(Regex("(?i)$title \\($year\\s?\\)")) && if(season!=null) it.third?.contains("-tvshow-") == true else it.third?.contains("-movie-") == true + it.first.contains(Regex("(?i)$title \\($year\\s?\\)")) && if (season != null) it.third?.contains( + "-tvshow-" + ) == true else it.third?.contains("-movie-") == true } } @@ -1527,10 +1518,16 @@ object SoraExtractor : SoraStream() { iframe?.apmap { val iframeDoc = app.get(it?.first ?: return@apmap).document val formUrl = iframeDoc.select("form").attr("action") - val formData = iframeDoc.select("form button").associate { v -> v.attr("name") to v.attr("value") } + val formData = + iframeDoc.select("form button").associate { v -> v.attr("name") to v.attr("value") } - val videoUrl = app.post(formUrl, data = formData, referer = it.first).document.selectFirst("div.d-flex.justify-content-center.flex-wrap a")?.attr("href") - val quality = Regex("(\\d{3,4})p").find(it.second)?.groupValues?.getOrNull(1)?.toIntOrNull() + val videoUrl = app.post( + formUrl, + data = formData, + referer = it.first + ).document.selectFirst("div.d-flex.justify-content-center.flex-wrap a")?.attr("href") + val quality = + Regex("(\\d{3,4})p").find(it.second)?.groupValues?.getOrNull(1)?.toIntOrNull() val qualityName = it.second.replace("${quality}p", "").trim() callback.invoke( @@ -1625,27 +1622,32 @@ object SoraExtractor : SoraStream() { } suspend fun invokeSmashyStream( - tmdbId: Int? = null, + imdbId: String? = null, season: Int? = null, episode: Int? = null, subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit, ) { val url = if (season == null) { - "$smashyStreamAPI/playere.php?tmdb=$tmdbId" + "$smashyStreamAPI/playere.php?imdb=$imdbId" } else { - "$smashyStreamAPI/playere.php?tmdb=$tmdbId&season=$season&episode=$episode" + "$smashyStreamAPI/playere.php?imdb=$imdbId&season=$season&episode=$episode" } app.get( - url, referer = "https://smashystream.xyz/" + url, referer = "https://smashystream.com/" ).document.select("div#_default-servers a.server").map { it.attr("data-url") to it.text() }.apmap { - when { - it.second.contains(Regex("(Player F|Player FM)\$")) -> { + when (it.second) { + "Player F" -> { invokeSmashyFfix(it.second, it.first, url, callback) } + + "Player D (Hindi)" -> { + invokeSmashyD(it.first, url, callback) + } + else -> return@apmap } } @@ -1920,14 +1922,15 @@ object SoraExtractor : SoraStream() { "$twoEmbedAPI/embedtv/$imdbId&s=$season&e=$episode" } - val framesrc = app.get(url).document.selectFirst("iframe#iframesrc")?.attr("data-src") ?: return + val framesrc = + app.get(url).document.selectFirst("iframe#iframesrc")?.attr("data-src") ?: return val ref = getBaseUrl(framesrc) val id = framesrc.substringAfter("id=").substringBefore("&") loadExtractor("https://wishfast.top/e/$id", "$ref/", subtitleCallback, callback) } - suspend fun invokeGomovies( + suspend fun invokeOmovies( title: String? = null, year: Int? = null, season: Int? = null, @@ -1940,10 +1943,10 @@ object SoraExtractor : SoraStream() { season, episode, callback, - "https://gomovies-online.cam", - "Gomovies", - "_smQamBQsETb", - "_sBWcqbTBMaT" + BuildConfig.OMOVIES_API, + "Omovies", + base64Decode("X3NtUWFtQlFzRVRi"), + base64Decode("X3NCV2NxYlRCTWFU") ) } @@ -1968,14 +1971,11 @@ object SoraExtractor : SoraStream() { } else { "$title Season $season" } - val idCookies = - mapOf( - "advanced-frontendgomovies7" to "bjd4n0nnv4hlt4fj5cdjgbrne2", - "_identitygomovies7" to "52fdc70b008c0b1d881dac0f01cca819edd512de01cc8bbc1224ed4aafb78b52a:2:{i:0;s:18:\"_identitygomovies7\";i:1;s:52:\"[2050366,\"HnVRRAObTASOJEr45YyCM8wiHol0V1ko\",2592000]\";}" + val savedCookies = mapOf( + "_identitygomovies7" to "52fdc70b008c0b1d881dac0f01cca819edd512de01cc8bbc1224ed4aafb78b52a%3A2%3A%7Bi%3A0%3Bs%3A18%3A%22_identitygomovies7%22%3Bi%3A1%3Bs%3A52%3A%22%5B2050366%2C%22HnVRRAObTASOJEr45YyCM8wiHol0V1ko%22%2C2592000%5D%22%3B%7D", ) val req = app.get("$api/search/$query") val doc = req.document - var cookies = req.cookies + idCookies val media = doc.select("div.$mediaSelector").map { Triple( it.attr("data-filmName"), it.attr("data-year"), it.select("a").attr("href") @@ -2003,9 +2003,8 @@ object SoraExtractor : SoraStream() { fixUrl( media.third, api - ), cookies = cookies + ) ) - cookies = cookies + res.cookies res.document.selectFirst("div#$episodeSelector a:contains(Episode ${slug.second})") ?.attr("href") } ?: return @@ -2016,11 +2015,11 @@ object SoraExtractor : SoraStream() { media.third.substringAfterLast("/") to iframe.substringAfterLast("/") .substringBefore("-") } - val res = app.get(fixUrl(iframe ?: return, api), cookies = cookies, verify = false) + val res = app.get(fixUrl(iframe, api), verify = false) val serverUrl = res.document.selectFirst("script:containsData(pushState)")?.data()?.let { """,\s*'([^']+)""".toRegex().find(it)?.groupValues?.get(1) } ?: return - cookies = cookies + res.cookies + val cookies = savedCookies + res.cookies 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) @@ -2064,7 +2063,6 @@ object SoraExtractor : SoraStream() { subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit, ) { - val ref = "https://blackvid.space/" val key = "b6055c533c19131a638c3d2299d525d5ec08a814" val url = if (season == null) { "$blackvidAPI/v3/movie/sources/$tmdbId?key=$key" @@ -2072,7 +2070,8 @@ object SoraExtractor : SoraStream() { "$blackvidAPI/v3/tv/sources/$tmdbId/$season/$episode?key=$key" } - val data = app.get(url, timeout = 120L, referer = ref).okhttpResponse.peekBody(1024 * 1024).bytes().decrypt("2378f8e4e844f2dc839ab48f66e00acc2305a401") + val data = request(url,).peekBody(1024 * 512).source().buffer.readByteArray() + .decrypt("2378f8e4e844f2dc839ab48f66e00acc2305a401") val json = tryParseJson(data) json?.sources?.map { source -> @@ -2082,7 +2081,7 @@ object SoraExtractor : SoraStream() { "Blackvid", "Blackvid${source.label}", s.url ?: return@s, - ref, + "https://blackvid.space/", if (s.quality.equals("4k")) Qualities.P2160.value else s.quality?.toIntOrNull() ?: Qualities.P1080.value, INFER_TYPE @@ -2163,7 +2162,7 @@ object SoraExtractor : SoraStream() { } - suspend fun invokeWatchOnline( + suspend fun invokeCinemaTv( imdbId: String? = null, title: String? = null, year: Int? = null, @@ -2175,17 +2174,19 @@ object SoraExtractor : SoraStream() { val id = imdbId?.removePrefix("tt") val slug = title.createSlug() val url = if (season == null) { - "$watchOnlineAPI/movies/play/$id-$slug-$year" + "$cinemaTvAPI/movies/play/$id-$slug-$year" } else { - "$watchOnlineAPI/shows/play/$id-$slug-$year" + "$cinemaTvAPI/shows/play/$id-$slug-$year" } - val doc = app.get( - url.replace( - watchOnlineAPI, - "https://ditairridgeleg.monster" - ) + "?mid=1&sid=9k9iupt5sebbnfajrc6ti3ht7l&sec=1974bc4a902c4d69fcbab261dcec69094a9b8164&t=1694986826984" - ).document + val specialCookies = "PHPSESSID=e555h63ilisoj2l6j7b5d4jb6p; _csrf=9597150e45f485ad9c4f2e06a2572534d8415337eda9d48d0ecfa25b73b6a9e1a%3A2%3A%7Bi%3A0%3Bs%3A5%3A%22_csrf%22%3Bi%3A1%3Bs%3A32%3A%222HcnegjGB0nX205FAUPb86fqMx9HWIF1%22%3B%7D; _ga=GA1.1.1195498587.1701871187; _ga_VZD7HJ3WK6=GS1.1.$unixTime.4.0.1.$unixTime.0.0.0" + val headers = mapOf( + "Cookie" to specialCookies, + "Connection" to "keep-alive", + "x-requested-with" to "XMLHttpRequest", + ) + + val doc = app.get(url, headers = headers).document val script = doc.selectFirst("script:containsData(hash:)")?.data() val hash = Regex("hash:\\s*['\"](\\S+)['\"]").find(script ?: return)?.groupValues?.get(1) val expires = Regex("expires:\\s*(\\d+)").find(script)?.groupValues?.get(1) @@ -2196,24 +2197,24 @@ object SoraExtractor : SoraStream() { }).let { it.toRegex().find(script)?.groupValues?.get(1) } val videoUrl = if (season == null) { - "$watchOnlineAPI/api/v1/security/movie-access?id_movie=$episodeId&hash=$hash&expires=$expires" + "$cinemaTvAPI/api/v1/security/movie-access?id_movie=$episodeId&hash=$hash&expires=$expires" } else { - "$watchOnlineAPI/api/v1/security/episode-access?id_episode=$episodeId&hash=$hash&expires=$expires" + "$cinemaTvAPI/api/v1/security/episode-access?id_episode=$episodeId&hash=$hash&expires=$expires" } val sources = app.get( videoUrl, referer = url, headers = mapOf("X-Requested-With" to "XMLHttpRequest") - ).parsedSafe() + ).parsedSafe() sources?.streams?.mapKeys { source -> callback.invoke( ExtractorLink( - "WatchOnline", - "WatchOnline", + "CinemaTv", + "CinemaTv", source.value, - "$watchOnlineAPI/", + "$cinemaTvAPI/", getQualityFromName(source.key), true ) @@ -2225,7 +2226,7 @@ object SoraExtractor : SoraStream() { subtitleCallback.invoke( SubtitleFile( sub.language ?: return@map, - if (file.startsWith("[")) return@map else fixUrl(file, watchOnlineAPI), + if (file.startsWith("[")) return@map else fixUrl(file, cinemaTvAPI), ) ) } @@ -2248,7 +2249,7 @@ object SoraExtractor : SoraStream() { val iframe = app.get(url, referer = "https://pressplay.top/").document.selectFirst("iframe") ?.attr("src") - NineTv.getUrl(iframe ?: return, "$nineTvAPI/", subtitleCallback, callback) + loadExtractor(iframe ?: return, "$nineTvAPI/", subtitleCallback, callback) } @@ -2261,9 +2262,11 @@ object SoraExtractor : SoraStream() { ) { val referer = "https://bflix.gs/" val slug = getEpisodeSlug(season, episode) - var url = if (season == null) "$nowTvAPI/$tmdbId.mp4" else "$nowTvAPI/tv/$tmdbId/s${season}e${slug.second}.mp4" + var url = + if (season == null) "$nowTvAPI/$tmdbId.mp4" else "$nowTvAPI/tv/$tmdbId/s${season}e${slug.second}.mp4" if (!app.get(url, referer = referer).isSuccessful) { - url = if (season == null) "$nowTvAPI/$imdbId.mp4" else "$nowTvAPI/tv/$imdbId/s${season}e${slug.second}.mp4" + url = + if (season == null) "$nowTvAPI/$imdbId.mp4" else "$nowTvAPI/tv/$imdbId/s${season}e${slug.second}.mp4" if (!app.get(url, referer = referer).isSuccessful) return } callback.invoke( @@ -2463,7 +2466,7 @@ object SoraExtractor : SoraStream() { ExtractorLink( "SFMovies", "SFMovies", - fixUrl(video, base64DecodeAPI("aQ==YXA=dGE=ZGE=c3Q=cmU=dC8=bmU=cy4=b3c=bmQ=d2k=ZS4=b3I=LmM=b2I=Ymw=aS4=YXA=dGE=ZGE=c3Q=cmU=Ly8=czo=dHA=aHQ=")), + fixUrl(video, getSfServer()), "", Qualities.P1080.value, INFER_TYPE diff --git a/SoraStream/src/main/kotlin/com/hexated/SoraParser.kt b/SoraStream/src/main/kotlin/com/hexated/SoraParser.kt index 980ceb46..229baa2f 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraParser.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraParser.kt @@ -152,14 +152,14 @@ data class JikanResponse( @JsonProperty("data") val data: JikanData? = null, ) -data class WatchOnlineSubtitles( +data class CinemaTvSubtitles( @JsonProperty("language") val language: String? = null, @JsonProperty("file") val file: Any? = null, ) -data class WatchOnlineResponse( +data class CinemaTvResponse( @JsonProperty("streams") val streams: HashMap? = null, - @JsonProperty("subtitles") val subtitles: ArrayList? = arrayListOf(), + @JsonProperty("subtitles") val subtitles: ArrayList? = arrayListOf(), ) data class VidsrctoResult( @@ -424,4 +424,13 @@ data class RidoSearch( data class SmashySources( @JsonProperty("sourceUrls") var sourceUrls: ArrayList? = arrayListOf(), @JsonProperty("subtitleUrls") var subtitleUrls: String? = null, +) + +data class SmashyDSources( + @JsonProperty("sourceUrls") var sourceUrls: ArrayList? = arrayListOf(), +) + +data class SmashyDSourcesUrls( + @JsonProperty("file") var file: String? = null, + @JsonProperty("title") var title: String? = null, ) \ No newline at end of file diff --git a/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt b/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt index 24337992..73b74d0b 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt @@ -43,7 +43,8 @@ import com.hexated.SoraExtractor.invokeTvMovies import com.hexated.SoraExtractor.invokeUhdmovies import com.hexated.SoraExtractor.invokeVegamovies import com.hexated.SoraExtractor.invokeVidsrcto -import com.hexated.SoraExtractor.invokeWatchOnline +import com.hexated.SoraExtractor.invokeCinemaTv +import com.hexated.SoraExtractor.invokeOmovies import com.hexated.SoraExtractor.invokeWatchsomuch import com.hexated.SoraExtractor.invokeZshow import com.lagradost.cloudstream3.LoadResponse.Companion.addImdbId @@ -93,10 +94,11 @@ open class SoraStream : TmdbProvider() { const val flixonAPI = "https://flixon.lol" const val smashyStreamAPI = "https://embed.smashystream.com" const val watchSomuchAPI = "https://watchsomuch.tv" // sub only - const val watchOnlineAPI = "https://lookmovie.foundation" + var cinemaTvAPI = BuildConfig.CINEMATV_API const val nineTvAPI = "https://moviesapi.club" const val nowTvAPI = "https://myfilestorage.xyz" const val gokuAPI = "https://goku.sx" + const val zshowAPI = BuildConfig.ZSHOW_API const val ridomoviesAPI = "https://ridomovies.pw" const val navyAPI = "https://navy-issue-i-239.site" const val emoviesAPI = "https://emovies.si" @@ -115,8 +117,8 @@ open class SoraStream : TmdbProvider() { const val uhdmoviesAPI = "https://uhdmovies.zip" const val gMoviesAPI = "https://gdrivemovies.xyz" const val hdmovies4uAPI = "https://hdmovies4u.band" - const val vegaMoviesAPI = "https://vegamovies.boo" - const val dotmoviesAPI = "https://dotmovies.im" + const val vegaMoviesAPI = "https://vegamovies.dad" + const val dotmoviesAPI = "https://dotmovies.bet" const val tvMoviesAPI = "https://www.tvseriesnmovies.com" const val moviezAddAPI = "https://ww3.moviezaddiction.click" const val bollyMazaAPI = "https://ww3.bollymaza.click" @@ -300,6 +302,7 @@ open class SoraStream : TmdbProvider() { this.showStatus = getStatus(res.status) this.recommendations = recommendations this.actors = actors + this.contentRating = fetchContentRating(data.id, "US") addTrailer(trailer) addTMDbId(data.id.toString()) addImdbId(res.external_ids?.imdb_id) @@ -334,6 +337,7 @@ open class SoraStream : TmdbProvider() { this.rating = rating this.recommendations = recommendations this.actors = actors + this.contentRating = fetchContentRating(data.id, "US") addTrailer(trailer) addTMDbId(data.id.toString()) addImdbId(res.external_ids?.imdb_id) @@ -351,6 +355,15 @@ open class SoraStream : TmdbProvider() { val res = parseJson(data) argamap( + { + if (!res.isAnime) invokeBlackvid( + res.id, + res.season, + res.episode, + subtitleCallback, + callback + ) + }, { invokeDumpStream( res.title, @@ -430,7 +443,7 @@ open class SoraStream : TmdbProvider() { ) }, { - invokeKisskh( + if (res.isAsian || res.isAnime) invokeKisskh( res.title, res.season, res.episode, @@ -440,6 +453,15 @@ open class SoraStream : TmdbProvider() { callback ) }, + { + if (!res.isAnime) invokeOmovies ( + res.title, + res.year, + res.season, + res.episode, + callback + ) + }, { if (!res.isAnime) invokeLing( res.title, @@ -455,7 +477,6 @@ open class SoraStream : TmdbProvider() { res.title, res.year, res.season, - res.lastSeason, res.episode, callback ) @@ -527,7 +548,7 @@ open class SoraStream : TmdbProvider() { }, { if (!res.isAnime) invokeSmashyStream( - res.id, + res.imdbId, res.season, res.episode, subtitleCallback, @@ -561,7 +582,7 @@ open class SoraStream : TmdbProvider() { ) }, { - invokeWatchOnline( + invokeCinemaTv( res.imdbId, res.title, res.airedYear ?: res.year, @@ -672,15 +693,6 @@ open class SoraStream : TmdbProvider() { callback ) }, - { - if (!res.isAnime) invokeBlackvid( - res.id, - res.season, - res.episode, - subtitleCallback, - callback - ) - }, { if (!res.isAnime) invokeShowflix( res.title, diff --git a/SoraStream/src/main/kotlin/com/hexated/SoraStreamLite.kt b/SoraStream/src/main/kotlin/com/hexated/SoraStreamLite.kt index ecc42e58..c3b4a827 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraStreamLite.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraStreamLite.kt @@ -29,7 +29,8 @@ import com.hexated.SoraExtractor.invokeSFMovies import com.hexated.SoraExtractor.invokeShowflix import com.hexated.SoraExtractor.invokeVidSrc import com.hexated.SoraExtractor.invokeVidsrcto -import com.hexated.SoraExtractor.invokeWatchOnline +import com.hexated.SoraExtractor.invokeCinemaTv +import com.hexated.SoraExtractor.invokeOmovies import com.hexated.SoraExtractor.invokeWatchsomuch import com.hexated.SoraExtractor.invokeZshow import com.lagradost.cloudstream3.SubtitleFile @@ -133,6 +134,15 @@ class SoraStreamLite : SoraStream() { callback ) }, + { + if (!res.isAnime) invokeOmovies( + res.title, + res.year, + res.season, + res.episode, + callback + ) + }, { if (!res.isAnime) invokeKimcartoon( res.title, @@ -144,7 +154,7 @@ class SoraStreamLite : SoraStream() { }, { if (!res.isAnime) invokeSmashyStream( - res.id, + res.imdbId, res.season, res.episode, subtitleCallback, @@ -161,7 +171,7 @@ class SoraStreamLite : SoraStream() { ) }, { - invokeKisskh( + if (res.isAsian || res.isAnime) invokeKisskh( res.title, res.season, res.episode, @@ -198,7 +208,7 @@ class SoraStreamLite : SoraStream() { if (!res.isAnime) invokeFlixon(res.id, res.imdbId, res.season, res.episode, callback) }, { - invokeWatchOnline( + invokeCinemaTv( res.imdbId, res.title, res.airedYear ?: res.year, diff --git a/SoraStream/src/main/kotlin/com/hexated/SoraStreamPlugin.kt b/SoraStream/src/main/kotlin/com/hexated/SoraStreamPlugin.kt index d8d2431a..4cfd8ef0 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraStreamPlugin.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraStreamPlugin.kt @@ -20,7 +20,6 @@ class SoraStreamPlugin: Plugin() { registerExtractorAPI(Playm4u()) registerExtractorAPI(VCloud()) registerExtractorAPI(Pixeldra()) - registerExtractorAPI(Hubcloud()) registerExtractorAPI(M4ufree()) registerExtractorAPI(Streamruby()) registerExtractorAPI(Streamwish()) @@ -28,5 +27,7 @@ class SoraStreamPlugin: Plugin() { registerExtractorAPI(Embedwish()) registerExtractorAPI(Wishfast()) registerExtractorAPI(Uploadever()) + registerExtractorAPI(Netembed()) + registerExtractorAPI(Vidplay2()) } } \ 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 e62f5006..f5895a6e 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraUtils.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraUtils.kt @@ -8,7 +8,6 @@ import com.hexated.SoraStream.Companion.filmxyAPI 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.watchflxAPI import com.lagradost.cloudstream3.* @@ -20,12 +19,16 @@ import com.lagradost.cloudstream3.utils.AppUtils.toJson import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson import com.lagradost.nicehttp.NiceResponse import com.lagradost.nicehttp.RequestBodyTypes +import com.lagradost.nicehttp.Requests.Companion.await import com.lagradost.nicehttp.requestCreator import kotlinx.coroutines.delay import okhttp3.FormBody import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.OkHttpClient +import okhttp3.Request import okhttp3.RequestBody.Companion.toRequestBody +import okhttp3.Response import org.jsoup.nodes.Document import java.math.BigInteger import java.net.* @@ -34,9 +37,8 @@ import java.security.* import java.security.spec.PKCS8EncodedKeySpec import java.security.spec.X509EncodedKeySpec import java.text.SimpleDateFormat -import java.time.LocalDate -import java.time.format.DateTimeFormatter import java.util.* +import java.util.concurrent.TimeUnit import javax.crypto.Cipher import javax.crypto.spec.GCMParameterSpec import javax.crypto.spec.IvParameterSpec @@ -45,7 +47,9 @@ import kotlin.collections.ArrayList import kotlin.math.min var watchflxCookies: Map? = null -var filmxyCookies: Map? = null +var filmxyCookies: Map? = null +var sfServer: String? = null + val encodedIndex = arrayOf( "GamMovies", "JSMovies", @@ -93,51 +97,6 @@ val mimeType = arrayOf( "video/x-msvideo" ) -fun String.filterIframe( - seasonNum: Int? = null, - lastSeason: Int? = null, - year: Int?, - title: String? -): Boolean { - val slug = title.createSlug() - val dotSlug = slug?.replace("-", ".") - val spaceSlug = slug?.replace("-", " ") - return if (seasonNum != null) { - if (lastSeason == 1) { - this.contains(Regex("(?i)(S0?$seasonNum)|(Season\\s0?$seasonNum)|(\\d{3,4}p)")) && !this.contains( - "Download", - true - ) - } else { - this.contains(Regex("(?i)(S0?$seasonNum)|(Season\\s0?$seasonNum)")) && !this.contains( - "Download", - true - ) - } - } else { - this.contains(Regex("(?i)($year)|($dotSlug)|($spaceSlug)")) && !this.contains( - "Download", - true - ) - } -} - -fun String.filterMedia(title: String?, yearNum: Int?, seasonNum: Int?): Boolean { - val fixTitle = title.createSlug()?.replace("-", " ") - return if (seasonNum != null) { - when { - seasonNum > 1 -> this.contains(Regex("(?i)(Season\\s0?1-0?$seasonNum)|(S0?1-S?0?$seasonNum)")) && this.contains( - Regex("(?i)($fixTitle)|($title)") - ) - else -> this.contains(Regex("(?i)(Season\\s0?1)|(S0?1)")) && this.contains( - Regex("(?i)($fixTitle)|($title)") - ) && this.contains("$yearNum") - } - } else { - this.contains(Regex("(?i)($fixTitle)|($title)")) && this.contains("$yearNum") - } -} - fun Document.getMirrorLink(): String? { return this.select("div.mb-4 a").randomOrNull() ?.attr("href") @@ -450,16 +409,28 @@ suspend fun invokeSmashyFfix( val json = app.get(url, referer = ref, headers = mapOf("X-Requested-With" to "XMLHttpRequest")) .parsedSafe() json?.sourceUrls?.map { - callback.invoke( - ExtractorLink( - "Smashy [$name]", - "Smashy [$name]", - it, - if(name == "Player FM") "https://vidplay.site/" else "", - Qualities.P1080.value, - INFER_TYPE - ) - ) + M3u8Helper.generateM3u8( + "Smashy [$name]", + it, + "" + ).forEach(callback) + } + +} + +suspend fun invokeSmashyD( + url: String, + ref: String, + callback: (ExtractorLink) -> Unit, +) { + val json = app.get(url, referer = ref, headers = mapOf("X-Requested-With" to "XMLHttpRequest")) + .parsedSafe() + json?.sourceUrls?.apmap { + M3u8Helper.generateM3u8( + "Smashy [Player D ${it.title}]", + it.file ?: return@apmap, + "" + ).forEach(callback) } } @@ -487,6 +458,7 @@ suspend fun getDumpIdAndType(title: String?, year: Int?, season: Int?): Pair { it.name?.contains( "$title", @@ -496,6 +468,7 @@ suspend fun getDumpIdAndType(title: String?, year: Int?, season: Int?): Pair { it.name?.contains(Regex("(?i)$title\\s?($season|${season.toRomanNumeral()}|Season\\s$season)")) == true && it.releaseTime == "$year" && it.domainType == 1 } @@ -531,25 +504,26 @@ suspend fun invokeDrivetot( ) { 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 + 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? { @@ -614,28 +588,27 @@ suspend fun bypassFdAds(url: String?): String? { } suspend fun bypassHrefli(url: String): String? { - val postUrl = url.substringBefore("?id=").substringAfter("/?") - val res = app.post( - postUrl, data = mapOf( - "_wp_http" to url.substringAfter("?id=") - ) - ).document + fun Document.getFormUrl() : String { + return this.select("form#landing").attr("action") + } + fun Document.getFormData() : Map { + return this.select("form#landing input").associate { it.attr("name") to it.attr("value") } + } - val link = res.select("form#landing").attr("action") - val wpHttp = res.select("input[name=_wp_http2]").attr("value") - val token = res.select("input[name=token]").attr("value") + val host = getBaseUrl(url) + var res = app.get(url).document + var formUrl = res.getFormUrl() + var formData = res.getFormData() - val blogRes = app.post( - link, data = mapOf( - "_wp_http2" to wpHttp, - "token" to token - ) - ).text + res = app.post(formUrl, data = formData).document + formUrl = res.getFormUrl() + formData = res.getFormData() - val skToken = blogRes.substringAfter("?go=").substringBefore("\"") + res = app.post(formUrl, data = formData).document + val skToken = res.selectFirst("script:containsData(?go=)")?.data()?.substringAfter("?go=")?.substringBefore("\"") ?: return null val driveUrl = app.get( - "$postUrl?go=$skToken", cookies = mapOf( - skToken to wpHttp + "$host?go=$skToken", cookies = mapOf( + skToken to "${formData["_wp_http2"]}" ) ).document.selectFirst("meta[http-equiv=refresh]")?.attr("content")?.substringAfter("url=") val path = app.get(driveUrl ?: return null).text.substringAfter("replace(\"") @@ -644,94 +617,6 @@ suspend fun bypassHrefli(url: String): String? { return fixUrl(path, getBaseUrl(driveUrl)) } -suspend fun bypassTechmny(url: String): String? { - val techRes = app.get(url).document - val postUrl = url.substringBefore("?id=").substringAfter("/?") - val (goUrl, goHeader) = if (techRes.selectFirst("form#landing input[name=_wp_http_c]") != null) { - var res = app.post( - postUrl, data = mapOf( - "_wp_http_c" to url.substringAfter("?id=") - ) - ) - val (longC, catC, _) = getTechmnyCookies(res.text) - var headers = mapOf("Cookie" to "$longC; $catC") - var formLink = res.document.selectFirst("center a")?.attr("href") - res = app.get(formLink ?: return null, headers = headers) - val (longC2, _, postC) = getTechmnyCookies(res.text) - headers = mapOf("Cookie" to "$catC; $longC2; $postC") - formLink = res.document.selectFirst("center a")?.attr("href") - - res = app.get(formLink ?: return null, headers = headers) - val goToken = res.text.substringAfter("?go=").substringBefore("\"") - val tokenUrl = "$postUrl?go=$goToken" - val newLongC = "$goToken=" + longC2.substringAfter("=") - headers = mapOf("Cookie" to "$catC; rdst_post=; $newLongC") - Pair(tokenUrl, headers) - } else { - val secondPage = techRes.getNextTechPage().document - val thirdPage = secondPage.getNextTechPage().text - val goToken = thirdPage.substringAfter("?go=").substringBefore("\"") - val tokenUrl = "$postUrl?go=$goToken" - val headers = mapOf( - "Cookie" to "$goToken=${ - secondPage.select("form#landing input[name=_wp_http2]").attr("value") - }" - ) - Pair(tokenUrl, headers) - } - val driveUrl = - app.get(goUrl, headers = goHeader).document.selectFirst("meta[http-equiv=refresh]") - ?.attr("content")?.substringAfter("url=") - val path = app.get(driveUrl ?: return null).text.substringAfter("replace(\"") - .substringBefore("\")") - if (path == "/404") return null - return fixUrl(path, getBaseUrl(driveUrl)) -} - -private suspend fun Document.getNextTechPage(): NiceResponse { - return app.post( - this.select("form").attr("action"), - data = this.select("form input").mapNotNull { - it.attr("name") to it.attr("value") - }.toMap().toMutableMap() - ) -} - -suspend fun bypassDriveleech(url: String): String? { - val path = app.get(url).text.substringAfter("replace(\"") - .substringBefore("\")") - if (path == "/404") return null - return fixUrl(path, getBaseUrl(url)) -} - -private fun getTechmnyCookies(page: String): Triple { - val cat = "rdst_cat" - val post = "rdst_post" - val longC = page.substringAfter(".setTime") - .substringAfter("document.cookie = \"") - .substringBefore("\"") - .substringBefore(";") - val catC = if (page.contains("$cat=")) { - page.substringAfterLast("$cat=") - .substringBefore(";").let { - "$cat=$it" - } - } else { - "" - } - - val postC = if (page.contains("$post=")) { - page.substringAfterLast("$post=") - .substringBefore(";").let { - "$post=$it" - } - } else { - "" - } - - return Triple(longC, catC, postC) -} - suspend fun getTvMoviesServer(url: String, season: Int?, episode: Int?): Pair? { val req = app.get(url) @@ -761,10 +646,20 @@ suspend fun getTvMoviesServer(url: String, season: Int?, episode: Int?): Pair { - val defaultCookies = mutableMapOf("G_ENABLED_IDPS" to "google", "true_checker" to "1", "XID" to "1") + val defaultCookies = + mutableMapOf("G_ENABLED_IDPS" to "google", "true_checker" to "1", "XID" to "1") session.get( url, headers = mapOf( @@ -777,7 +672,10 @@ suspend fun fetchFilmxyCookies(url: String): Map { defaultCookies["PHPSESSID"] = phpsessid val userNonce = - app.get("$filmxyAPI/login/?redirect_to=$filmxyAPI/", cookies = defaultCookies).document.select("script") + app.get( + "$filmxyAPI/login/?redirect_to=$filmxyAPI/", + cookies = defaultCookies + ).document.select("script") .find { it.data().contains("var userNonce") }?.data()?.let { Regex("var\\suserNonce.*?\"(\\S+?)\";").find(it)?.groupValues?.get(1) } @@ -801,7 +699,8 @@ suspend fun fetchFilmxyCookies(url: String): Map { return cookieJar.plus(defaultCookies) } -suspend fun getWatchflxCookies() = watchflxCookies ?: fetchWatchflxCookies().also { watchflxCookies = it } +suspend fun getWatchflxCookies() = + watchflxCookies ?: fetchWatchflxCookies().also { watchflxCookies = it } suspend fun fetchWatchflxCookies(): Map { session.get(watchflxAPI) @@ -813,7 +712,8 @@ suspend fun fetchWatchflxCookies(): Map { "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 } + return session.baseClient.cookieJar.loadForRequest(loginUrl.toHttpUrl()) + .associate { it.name to it.value } } fun Document.findTvMoviesIframe(): String? { @@ -1148,7 +1048,11 @@ fun String.decodePrimewireXor(key: String): String { fun vidsrctoDecrypt(text: String): String { val parse = Base64.decode(text.toByteArray(), Base64.URL_SAFE) val cipher = Cipher.getInstance("RC4") - cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec("8z5Ag5wgagfsOuhz".toByteArray(), "RC4"), cipher.parameters) + cipher.init( + Cipher.DECRYPT_MODE, + SecretKeySpec("8z5Ag5wgagfsOuhz".toByteArray(), "RC4"), + cipher.parameters + ) return decode(cipher.doFinal(parse).toString(Charsets.UTF_8)) } @@ -1288,7 +1192,7 @@ fun isUpcoming(dateString: String?): Boolean { } } -fun getDate() : TmdbDate { +fun getDate(): TmdbDate { val formatter = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()) val calender = Calendar.getInstance() val today = formatter.format(calender.time) @@ -1373,12 +1277,31 @@ private enum class Symbol(val decimalValue: Int) { companion object { fun closestBelow(value: Int) = - values() + entries.toTypedArray() .sortedByDescending { it.decimalValue } .firstOrNull { value >= it.decimalValue } } } +suspend fun request( + url: String, + allowRedirects: Boolean = true, + timeout: Long = 60L +): Response { + val client = OkHttpClient().newBuilder() + .connectTimeout(timeout, TimeUnit.SECONDS) + .readTimeout(timeout, TimeUnit.SECONDS) + .writeTimeout(timeout, TimeUnit.SECONDS) + .followRedirects(allowRedirects) + .followSslRedirects(allowRedirects) + .build() + + val request: Request = Request.Builder() + .url(url) + .build() + return client.newCall(request).await() +} + object DumpUtils { private val deviceId = getDeviceId() @@ -1644,4 +1567,4 @@ object AESGCM { dateFormat.timeZone = TimeZone.getTimeZone("GMT") return dateFormat.format(Date()) } -} \ No newline at end of file +} diff --git a/StremioX/build.gradle.kts b/StremioX/build.gradle.kts index fb6333b2..c08ea2b1 100644 --- a/StremioX/build.gradle.kts +++ b/StremioX/build.gradle.kts @@ -1,5 +1,5 @@ // use an integer for version numbers -version = 11 +version = 12 cloudstream { diff --git a/StremioX/src/main/kotlin/com/hexated/StremioX.kt b/StremioX/src/main/kotlin/com/hexated/StremioX.kt index 7f2419fe..314e124f 100644 --- a/StremioX/src/main/kotlin/com/hexated/StremioX.kt +++ b/StremioX/src/main/kotlin/com/hexated/StremioX.kt @@ -13,8 +13,9 @@ import com.lagradost.cloudstream3.utils.AppUtils.toJson import java.net.URI import java.util.ArrayList import kotlin.math.roundToInt +import com.lagradost.cloudstream3.metaproviders.TmdbProvider -open class StremioX : MainAPI() { +open class StremioX : TmdbProvider() { override var mainUrl = "https://torrentio.strem.fun" override var name = "StremioX" override val hasMainPage = true @@ -181,6 +182,7 @@ open class StremioX : MainAPI() { this.showStatus = getStatus(res.status) this.recommendations = recommendations this.actors = actors + this.contentRating = fetchContentRating(data.id, "US") addTrailer(trailer) addTMDbId(data.id.toString()) addImdbId(res.external_ids?.imdb_id) @@ -202,6 +204,7 @@ open class StremioX : MainAPI() { this.rating = rating this.recommendations = recommendations this.actors = actors + this.contentRating = fetchContentRating(data.id, "US") addTrailer(trailer) addTMDbId(data.id.toString()) addImdbId(res.external_ids?.imdb_id) diff --git a/YomoviesProvider/build.gradle.kts b/YomoviesProvider/build.gradle.kts index 9519ad57..7934b290 100644 --- a/YomoviesProvider/build.gradle.kts +++ b/YomoviesProvider/build.gradle.kts @@ -1,12 +1,12 @@ // use an integer for version numbers -version = 24 +version = 25 cloudstream { language = "hi" // All of these properties are optional, you can safely remove them - description = "Include: Watchomovies" + description = "Includes: Watchomovies(NS*W), Max resolution is 720p in both extensions." authors = listOf("Hexated") /** diff --git a/YomoviesProvider/src/main/kotlin/com/hexated/Watchomovies.kt b/YomoviesProvider/src/main/kotlin/com/hexated/Watchomovies.kt index c2f77274..a184b23e 100644 --- a/YomoviesProvider/src/main/kotlin/com/hexated/Watchomovies.kt +++ b/YomoviesProvider/src/main/kotlin/com/hexated/Watchomovies.kt @@ -5,7 +5,7 @@ import com.lagradost.cloudstream3.TvType import com.lagradost.cloudstream3.mainPageOf class Watchomovies : YomoviesProvider() { - override var mainUrl = "https://watchomovies.beauty" + override var mainUrl = "https://watchomovies.lat" override var name = "Watchomovies" override var lang = "en" override val supportedTypes = setOf( diff --git a/YomoviesProvider/src/main/kotlin/com/hexated/YomoviesProvider.kt b/YomoviesProvider/src/main/kotlin/com/hexated/YomoviesProvider.kt index 283389b5..d84867d4 100644 --- a/YomoviesProvider/src/main/kotlin/com/hexated/YomoviesProvider.kt +++ b/YomoviesProvider/src/main/kotlin/com/hexated/YomoviesProvider.kt @@ -10,7 +10,7 @@ import org.jsoup.nodes.Element import java.net.URI open class YomoviesProvider : MainAPI() { - override var mainUrl = "https://yomovies.show" + override var mainUrl = "https://yomovies.media" private var directUrl = "" override var name = "Yomovies" override val hasMainPage = true diff --git a/build.gradle.kts b/build.gradle.kts index b71e2492..c59fbf5d 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -13,7 +13,7 @@ buildscript { classpath("com.android.tools.build:gradle:7.0.4") // Cloudstream gradle plugin which makes everything work and builds plugins classpath("com.github.recloudstream:gradle:-SNAPSHOT") - classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.20") + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.21") } }