From f4f5ee5f5be069a810870453be2a2931949eedbe Mon Sep 17 00:00:00 2001 From: hexated Date: Tue, 26 Sep 2023 12:45:33 +0700 Subject: [PATCH] fixed #295 --- Gomov/build.gradle.kts | 2 +- .../src/main/kotlin/com/hexated/Extractors.kt | 65 +++++++++++++++++++ Gomov/src/main/kotlin/com/hexated/Gomov.kt | 56 ++++++++++------ SoraStream/build.gradle.kts | 2 +- .../src/main/kotlin/com/hexated/Extractors.kt | 63 +++++++++++++++++- .../main/kotlin/com/hexated/SoraExtractor.kt | 14 ++-- .../src/main/kotlin/com/hexated/SoraStream.kt | 4 +- .../main/kotlin/com/hexated/SoraStreamLite.kt | 2 +- .../src/main/kotlin/com/hexated/SoraUtils.kt | 9 ++- 9 files changed, 184 insertions(+), 33 deletions(-) diff --git a/Gomov/build.gradle.kts b/Gomov/build.gradle.kts index 96c2fbf6..c53fc967 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 = 15 +version = 16 android { defaultConfig { diff --git a/Gomov/src/main/kotlin/com/hexated/Extractors.kt b/Gomov/src/main/kotlin/com/hexated/Extractors.kt index aa629966..039b639b 100644 --- a/Gomov/src/main/kotlin/com/hexated/Extractors.kt +++ b/Gomov/src/main/kotlin/com/hexated/Extractors.kt @@ -1,6 +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.app import com.lagradost.cloudstream3.extractors.* +import com.lagradost.cloudstream3.extractors.helper.AesHelper +import com.lagradost.cloudstream3.utils.AppUtils +import com.lagradost.cloudstream3.utils.ExtractorLink +import com.lagradost.cloudstream3.utils.M3u8Helper class Doods : DoodLaExtractor() { override var name = "Doods" @@ -34,4 +42,61 @@ class Likessb : StreamSB() { class DbGdriveplayer : Gdriveplayer() { override var mainUrl = "https://database.gdriveplayer.us" +} + +class NineTv { + + companion object { + private const val key = "B#8G4o2\$WWFz" + suspend fun getUrl( + url: String, + referer: String?, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ) { + val mainUrl = getBaseUrl(url) + val master = Regex("MasterJS\\s*=\\s*'([^']+)").find( + app.get( + url, + referer = referer + ).text + )?.groupValues?.get(1) + val decrypt = AesHelper.cryptoAESHandler(master ?: return, key.toByteArray(), false) + ?.replace("\\", "") ?: throw ErrorLoadingException("failed to decrypt") + + val name = url.getHost() + val source = Regex(""""?file"?:\s*"([^"]+)""").find(decrypt)?.groupValues?.get(1) + val tracks = Regex("""tracks:\s*\[(.+)]""").find(decrypt)?.groupValues?.get(1) + + 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 + ) + ) + } + } + } + + 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 f539fb76..bb027326 100644 --- a/Gomov/src/main/kotlin/com/hexated/Gomov.kt +++ b/Gomov/src/main/kotlin/com/hexated/Gomov.kt @@ -21,6 +21,8 @@ open class Gomov : MainAPI() { TvType.AsianDrama ) + 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", "category/western-series/page/%d/" to "Western Series", @@ -154,19 +156,31 @@ open class Gomov : MainAPI() { val id = document.selectFirst("div#muvipro_player_content_id")?.attr("data-id") if(id.isNullOrEmpty()) { - document.select("ul.muvipro-player-tabs li a").apmap { - val iframe = app.get(fixUrl(it.attr("href"))).document.selectFirst("div.gmr-embed-responsive iframe") - ?.attr("src") - loadExtractor(httpsify(iframe ?: return@apmap ), "$directUrl/", subtitleCallback, callback) + document.select("ul.muvipro-player-tabs li a").apmap { ele -> + val iframe = app.get(fixUrl(ele.attr("href"))).document.selectFirst("div.gmr-embed-responsive iframe") + ?.attr("src")?.let { httpsify(it) } ?: return@apmap + + when { + sources.any { iframe.startsWith(it) } -> NineTv.getUrl(iframe, "$directUrl/", subtitleCallback, callback) + else -> { + loadExtractor(iframe, "$directUrl/", subtitleCallback, callback) + } + } } } else { - document.select("div.tab-content-ajax").apmap { + document.select("div.tab-content-ajax").apmap { ele -> val server = app.post( "$directUrl/wp-admin/admin-ajax.php", - data = mapOf("action" to "muvipro_player_content", "tab" to it.attr("id"), "post_id" to "$id") - ).document.select("iframe").attr("src") + 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(httpsify(server), "$directUrl/", subtitleCallback, callback) } } @@ -174,17 +188,21 @@ open class Gomov : MainAPI() { } - private fun String?.fixImageQuality(): String? { - if(this == null) return null - val regex = Regex("(-\\d*x\\d*)").find(this)?.groupValues - if(regex?.isEmpty() == true) return this - return this.replace(regex?.get(0) ?: return null, "") - } +} - private fun getBaseUrl(url: String): String { - return URI(url).let { - "${it.scheme}://${it.host}" - } - } +fun String?.fixImageQuality(): String? { + if(this == null) return null + val regex = Regex("(-\\d*x\\d*)").find(this)?.groupValues + if(regex?.isEmpty() == true) return this + return this.replace(regex?.get(0) ?: return null, "") +} +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/SoraStream/build.gradle.kts b/SoraStream/build.gradle.kts index 702b4bf1..b2d711ac 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 = 174 +version = 175 android { defaultConfig { diff --git a/SoraStream/src/main/kotlin/com/hexated/Extractors.kt b/SoraStream/src/main/kotlin/com/hexated/Extractors.kt index 9dc10e24..eb034448 100644 --- a/SoraStream/src/main/kotlin/com/hexated/Extractors.kt +++ b/SoraStream/src/main/kotlin/com/hexated/Extractors.kt @@ -6,14 +6,15 @@ 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.utils.* import java.math.BigInteger -import java.net.URI import java.security.MessageDigest open class Playm4u : ExtractorApi() { @@ -234,6 +235,63 @@ open class VCloud : ExtractorApi() { } +class NineTv { + + companion object { + private const val key = "B#8G4o2\$WWFz" + suspend fun getUrl( + url: String, + referer: String?, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ) { + val mainUrl = getBaseUrl(url) + val master = Regex("MasterJS\\s*=\\s*'([^']+)").find( + app.get( + url, + referer = referer + ).text + )?.groupValues?.get(1) + val decrypt = AesHelper.cryptoAESHandler(master ?: return, key.toByteArray(), false) + ?.replace("\\", "") ?: throw ErrorLoadingException("failed to decrypt") + + val name = url.getHost() + val source = Regex(""""?file"?:\s*"([^"]+)""").find(decrypt)?.groupValues?.get(1) + val tracks = Regex("""tracks:\s*\[(.+)]""").find(decrypt)?.groupValues?.get(1) + + 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 + ) + ) + } + } + } + + data class Tracks( + @JsonProperty("file") val file: String? = null, + @JsonProperty("label") val label: String? = null, + @JsonProperty("kind") val kind: String? = null, + ) +} + class Hubcloud : VCloud() { override val name = "Hubcloud" override val mainUrl = "https://hubcloud.in" @@ -271,4 +329,5 @@ class MultimoviesSB : StreamSB() { class Yipsu : Voe() { override val name = "Yipsu" override var mainUrl = "https://yip.su" -} \ 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 170fa271..3c2d9ddf 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt @@ -304,8 +304,12 @@ object SoraExtractor : SoraStream() { else -> it.embed_url } } ?: return@apmap - if (!source.contains("youtube")) { - loadCustomExtractor(name, source, "$referer/", subtitleCallback, callback) + val sources = arrayOf("https://chillx.top", "https://watchx.top", "https://bestx.stream") + when { + sources.any { source.startsWith(it) } -> NineTv.getUrl(source, "$referer/", subtitleCallback, callback) + !source.contains("youtube") -> { + loadCustomExtractor(name, source, "$referer/", subtitleCallback, callback) + } } } } @@ -2289,9 +2293,9 @@ object SoraExtractor : SoraStream() { "$nineTvAPI/tv/$tmdbId-$season-$episode" } - val iframe = app.get(url, referer = "https://pressplay.top/").document.selectFirst("iframe") - ?.attr("src") ?: return - loadExtractor(iframe, "$nineTvAPI/", subtitleCallback, callback) + val iframe = app.get(url, referer = "https://pressplay.top/").document.selectFirst("iframe")?.attr("src") + + NineTv.getUrl(iframe ?: return, "$nineTvAPI/", subtitleCallback, callback) } diff --git a/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt b/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt index a5cae47e..7817bd61 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt @@ -118,7 +118,7 @@ open class SoraStream : TmdbProvider() { const val emoviesAPI = "https://emovies.si" const val pobmoviesAPI = "https://pobmovies.cam" const val multimoviesAPI = "https://multi-movies.xyz" - const val netmoviesAPI = "https://web.netmovies.to" + const val netmoviesAPI = "https://netmovies.to" const val momentAPI = "https://moment-explanation-i-244.site" const val doomoviesAPI = "https://doomovies.net" const val primewireAPI = "https://real-primewire.club" @@ -626,7 +626,7 @@ open class SoraStream : TmdbProvider() { if (!res.isAnime) invokeNowTv(res.id, res.season, res.episode, callback) }, { - if (res.season == null) invokeRidomovies(res.title, res.year, callback) + if (!res.isAnime && res.season == null) invokeRidomovies(res.title, res.year, callback) }, { invokeNavy(res.imdbId, res.season, res.episode, callback) diff --git a/SoraStream/src/main/kotlin/com/hexated/SoraStreamLite.kt b/SoraStream/src/main/kotlin/com/hexated/SoraStreamLite.kt index 8a9745dc..a54b76ca 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraStreamLite.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraStreamLite.kt @@ -231,7 +231,7 @@ class SoraStreamLite : SoraStream() { invokeNavy(res.imdbId, res.season, res.episode, callback) }, { - if (res.season == null) invokeRidomovies( + if (!res.isAnime && res.season == null) invokeRidomovies( res.title, res.year, callback diff --git a/SoraStream/src/main/kotlin/com/hexated/SoraUtils.kt b/SoraStream/src/main/kotlin/com/hexated/SoraUtils.kt index 42694143..16dec755 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraUtils.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraUtils.kt @@ -1079,8 +1079,9 @@ suspend fun loadCustomExtractor( name ?: link.name, link.url, link.referer, - when (link.type) { - ExtractorLinkType.M3U8 -> link.quality + when { + link.type == ExtractorLinkType.M3U8 -> link.quality + link.name == "VidSrc" -> Qualities.P1080.value else -> quality ?: link.quality }, link.type, @@ -1321,6 +1322,10 @@ fun getBaseUrl(url: String): String { } } +fun String.getHost(): String { + return fixTitle(URI(this).host.substringBeforeLast(".").substringAfterLast(".")) +} + fun isUpcoming(dateString: String?): Boolean { return try { val format = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())