diff --git a/Animasu/build.gradle.kts b/Animasu/build.gradle.kts index 4b81579b..4c3d51d1 100644 --- a/Animasu/build.gradle.kts +++ b/Animasu/build.gradle.kts @@ -1,5 +1,5 @@ // use an integer for version numbers -version = 1 +version = 2 cloudstream { diff --git a/Animasu/src/main/kotlin/com/hexated/Animasu.kt b/Animasu/src/main/kotlin/com/hexated/Animasu.kt index e693a672..e6e38833 100644 --- a/Animasu/src/main/kotlin/com/hexated/Animasu.kt +++ b/Animasu/src/main/kotlin/com/hexated/Animasu.kt @@ -150,8 +150,8 @@ class Animasu : MainAPI() { link.name, link.url, link.referer, - if(!link.isM3u8) getIndexQuality(quality) else link.quality, - link.isM3u8, + if(link.type != ExtractorLinkType.M3U8) getIndexQuality(quality) else link.quality, + link.type, link.headers, link.extractorData ) diff --git a/AnimeIndoProvider/build.gradle.kts b/AnimeIndoProvider/build.gradle.kts index e2cc3499..d52cf888 100644 --- a/AnimeIndoProvider/build.gradle.kts +++ b/AnimeIndoProvider/build.gradle.kts @@ -1,5 +1,5 @@ // use an integer for version numbers -version = 12 +version = 13 cloudstream { diff --git a/AnimeIndoProvider/src/main/kotlin/com/hexated/AnimeIndoProvider.kt b/AnimeIndoProvider/src/main/kotlin/com/hexated/AnimeIndoProvider.kt index 91f8de5e..aee7726e 100644 --- a/AnimeIndoProvider/src/main/kotlin/com/hexated/AnimeIndoProvider.kt +++ b/AnimeIndoProvider/src/main/kotlin/com/hexated/AnimeIndoProvider.kt @@ -60,22 +60,22 @@ class AnimeIndoProvider : MainAPI() { return if (uri.contains("/anime/")) { uri } else { - var title = uri.substringAfter("nonton/") + var title = uri.substringAfter("$mainUrl/") title = when { - (title.contains("-episode")) && !(title.contains("-movie")) -> Regex("(.+)-episode").find( - title - )?.groupValues?.get(1).toString() - (title.contains("-movie")) -> Regex("(.+)-movie").find(title)?.groupValues?.get( - 1 - ).toString() + (title.contains("-episode")) && !(title.contains("-movie")) -> title.substringBefore( + "-episode" + ) + + (title.contains("-movie")) -> title.substringBefore("-movie") else -> title } + "$mainUrl/anime/$title" } } private fun Element.toSearchResult(): AnimeSearchResponse { - val title = this.selectFirst("div.titlex, h2.entry-title, h4")?.text()?.trim() ?: "" + val title = this.selectFirst("div.title, h2.entry-title, h4")?.text()?.trim() ?: "" val href = getProperAnimeLink(this.selectFirst("a")!!.attr("href")) val posterUrl = fixUrlNull(this.selectFirst("img")?.attr("src")) val epNum = this.selectFirst("span.episode")?.ownText()?.replace(Regex("\\D"), "")?.trim() diff --git a/AnimeSailProvider/build.gradle.kts b/AnimeSailProvider/build.gradle.kts index d0914891..73173634 100644 --- a/AnimeSailProvider/build.gradle.kts +++ b/AnimeSailProvider/build.gradle.kts @@ -1,5 +1,5 @@ // use an integer for version numbers -version = 8 +version = 9 cloudstream { diff --git a/AnimeSailProvider/src/main/kotlin/com/hexated/AnimeSailProvider.kt b/AnimeSailProvider/src/main/kotlin/com/hexated/AnimeSailProvider.kt index 45094af4..da62e664 100644 --- a/AnimeSailProvider/src/main/kotlin/com/hexated/AnimeSailProvider.kt +++ b/AnimeSailProvider/src/main/kotlin/com/hexated/AnimeSailProvider.kt @@ -3,6 +3,7 @@ package com.hexated import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.mvvm.safeApiCall import com.lagradost.cloudstream3.utils.ExtractorLink +import com.lagradost.cloudstream3.utils.ExtractorLinkType import com.lagradost.cloudstream3.utils.Qualities import com.lagradost.cloudstream3.utils.loadExtractor import com.lagradost.nicehttp.NiceResponse @@ -144,7 +145,7 @@ class AnimeSailProvider : MainAPI() { Jsoup.parse(base64Decode(it.attr("data-em"))).select("iframe").attr("src") ?: throw ErrorLoadingException("No iframe found") ) - + val quality = getIndexQuality(it.text()) when { iframe.startsWith("$mainUrl/utils/player/arch/") || iframe.startsWith( "$mainUrl/utils/player/race/" @@ -156,15 +157,13 @@ class AnimeSailProvider : MainAPI() { iframe.contains("/race/") -> "Race" else -> this.name } - val quality = - Regex("\\.(\\d{3,4})\\.").find(link)?.groupValues?.get(1) callback.invoke( ExtractorLink( source = source, name = source, url = link, referer = mainUrl, - quality = quality?.toIntOrNull() ?: Qualities.Unknown.value + quality = quality ) ) } @@ -175,16 +174,16 @@ class AnimeSailProvider : MainAPI() { val link = "https://rasa-cintaku-semakin-berantai.xyz/v/${ iframe.substringAfter("id=").substringBefore("&token") }" - loadExtractor(link, mainUrl, subtitleCallback, callback) + loadFixedExtractor(link, quality, mainUrl, subtitleCallback, callback) } iframe.startsWith("$mainUrl/utils/player/framezilla/") || iframe.startsWith("https://uservideo.xyz") -> { request(iframe, ref = data).document.select("iframe").attr("src") .let { link -> - loadExtractor(fixUrl(link), mainUrl, subtitleCallback, callback) + loadFixedExtractor(fixUrl(link), quality, mainUrl, subtitleCallback, callback) } } else -> { - loadExtractor(iframe, mainUrl, subtitleCallback, callback) + loadFixedExtractor(iframe, quality, mainUrl, subtitleCallback, callback) } } } @@ -193,4 +192,32 @@ class AnimeSailProvider : MainAPI() { return true } + private suspend fun loadFixedExtractor( + url: String, + quality: Int?, + referer: String? = null, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ) { + loadExtractor(url, referer, subtitleCallback) { link -> + callback.invoke( + ExtractorLink( + link.name, + link.name, + link.url, + link.referer, + if(link.type == ExtractorLinkType.M3U8) link.quality else quality ?: Qualities.Unknown.value, + link.type, + link.headers, + link.extractorData + ) + ) + } + } + + private fun getIndexQuality(str: String): Int { + return Regex("(\\d{3,4})[pP]").find(str)?.groupValues?.getOrNull(1)?.toIntOrNull() + ?: Qualities.Unknown.value + } + } \ No newline at end of file diff --git a/Aniworld/build.gradle.kts b/Aniworld/build.gradle.kts index d303a028..86c61272 100644 --- a/Aniworld/build.gradle.kts +++ b/Aniworld/build.gradle.kts @@ -1,5 +1,5 @@ // use an integer for version numbers -version = 5 +version = 6 cloudstream { diff --git a/Aniworld/src/main/kotlin/com/hexated/Aniworld.kt b/Aniworld/src/main/kotlin/com/hexated/Aniworld.kt index a5c6b08f..f55ac59e 100644 --- a/Aniworld/src/main/kotlin/com/hexated/Aniworld.kt +++ b/Aniworld/src/main/kotlin/com/hexated/Aniworld.kt @@ -158,7 +158,7 @@ open class Aniworld : MainAPI() { link.url, link.referer, link.quality, - link.isM3u8, + link.type, link.headers, link.extractorData ) diff --git a/Gomov/build.gradle.kts b/Gomov/build.gradle.kts index b66e81e1..cae88ee2 100644 --- a/Gomov/build.gradle.kts +++ b/Gomov/build.gradle.kts @@ -1,12 +1,12 @@ // use an integer for version numbers -version = 8 +version = 11 cloudstream { language = "id" // All of these properties are optional, you can safely remove them - description = "Includes: DutaMovie, Ngefilm, Nodrakorid" + description = "Includes: DutaMovie, Ngefilm, Nodrakorid, Multiplex" authors = listOf("Hexated") /** diff --git a/Gomov/src/main/kotlin/com/hexated/Extractors.kt b/Gomov/src/main/kotlin/com/hexated/Extractors.kt index 7017d7fa..aa629966 100644 --- a/Gomov/src/main/kotlin/com/hexated/Extractors.kt +++ b/Gomov/src/main/kotlin/com/hexated/Extractors.kt @@ -1,8 +1,11 @@ package com.hexated -import com.lagradost.cloudstream3.extractors.Filesim -import com.lagradost.cloudstream3.extractors.Gdriveplayer -import com.lagradost.cloudstream3.extractors.StreamSB +import com.lagradost.cloudstream3.extractors.* + +class Doods : DoodLaExtractor() { + override var name = "Doods" + override var mainUrl = "https://doods.pro" +} class Dutamovie21 : StreamSB() { override var name = "Dutamovie21" diff --git a/Gomov/src/main/kotlin/com/hexated/GomovPlugin.kt b/Gomov/src/main/kotlin/com/hexated/GomovPlugin.kt index 975337d4..8ee692f2 100644 --- a/Gomov/src/main/kotlin/com/hexated/GomovPlugin.kt +++ b/Gomov/src/main/kotlin/com/hexated/GomovPlugin.kt @@ -13,11 +13,13 @@ class GomovPlugin: Plugin() { registerMainAPI(DutaMovie()) registerMainAPI(Ngefilm()) registerMainAPI(Nodrakorid()) + registerMainAPI(Multiplex()) registerExtractorAPI(FilelionsTo()) registerExtractorAPI(Likessb()) registerExtractorAPI(DbGdriveplayer()) registerExtractorAPI(Dutamovie21()) registerExtractorAPI(Embedwish()) + registerExtractorAPI(Doods()) registerExtractorAPI(Lylxan()) } } \ No newline at end of file diff --git a/Gomov/src/main/kotlin/com/hexated/Multiplex.kt b/Gomov/src/main/kotlin/com/hexated/Multiplex.kt new file mode 100644 index 00000000..e45af7d2 --- /dev/null +++ b/Gomov/src/main/kotlin/com/hexated/Multiplex.kt @@ -0,0 +1,15 @@ +package com.hexated + +import com.lagradost.cloudstream3.mainPageOf + +class Multiplex : DutaMovie() { + override var mainUrl = "http://5.104.81.46" + override var name = "Multiplex" + + override val mainPage = mainPageOf( + "country/usa/page/%d/" to "Movie", + "west-series/page/%d/" to "West Series", + "nonton-drama-korea/page/%d/" to "Drama Korea", + ) + +} \ No newline at end of file diff --git a/Gomov/src/main/kotlin/com/hexated/Nodrakorid.kt b/Gomov/src/main/kotlin/com/hexated/Nodrakorid.kt index c65d6b2f..afc8e4da 100644 --- a/Gomov/src/main/kotlin/com/hexated/Nodrakorid.kt +++ b/Gomov/src/main/kotlin/com/hexated/Nodrakorid.kt @@ -26,16 +26,30 @@ class Nodrakorid : DutaMovie() { is TvSeriesLoadResponse -> { val doc = app.get(url).document this.comingSoon = false - this.episodes = doc.select("div.entry-content p:contains(Episode)").distinctBy { - it.text() - }.map { eps -> - val num = eps.text() - val endSibling = eps.nextElementSiblings().select("p:contains(Episode)").firstOrNull() ?: eps.nextElementSiblings().select("div.content-moviedata").firstOrNull() - val siblings = eps.nextElementSiblingsUntil(endSibling).map { ele -> - ele.ownText().filter { it.isDigit() }.toIntOrNull() to ele.select("a") - .map { it.attr("href") to it.text() } - }.filter { it.first != null } - Episode(siblings.toJson(), episode = Regex("Episode\\s?([0-9]+)").find(num)?.groupValues?.getOrNull(1)?.toIntOrNull()) + this.episodes = when { + doc.select("div.vid-episodes a, div.gmr-listseries a").isNotEmpty() -> this.episodes + doc.select("div#download").isEmpty() -> { + doc.select("div.entry-content p:contains(Episode)").distinctBy { + it.text() + }.mapNotNull { eps -> + val endSibling = eps.nextElementSiblings().select("p:contains(Episode)").firstOrNull() ?: eps.nextElementSiblings().select("div.content-moviedata").firstOrNull() + val siblings = eps.nextElementSiblingsUntil(endSibling).map { ele -> + ele.ownText().filter { it.isDigit() }.toIntOrNull() to ele.select("a") + .map { it.attr("href") to it.text() } + }.filter { it.first != null } + Episode(siblings.toJson(), episode = eps.text().toEpisode()) + } + } + else -> { + doc.select("div#download h3.title-download").mapNotNull { eps -> + val siblings = eps.nextElementSibling()?.select("li")?.map { ele -> + ele.text().filter { it.isDigit() }.toIntOrNull() to ele.select("a").map { + it.attr("href") to it.text().split(" ").first() + } + }?.filter { it.first != null } + Episode(siblings?.toJson() ?: return@mapNotNull null, episode = eps.text().toEpisode()) + } + } } } } @@ -83,6 +97,10 @@ class Nodrakorid : DutaMovie() { } } + private fun String.toEpisode() : Int? { + return Regex("(?i)Episode\\s?([0-9]+)").find(this)?.groupValues?.getOrNull(1)?.toIntOrNull() + } + private fun getBaseUrl(url: String): String { return URI(url).let { "${it.scheme}://${it.host}" @@ -103,8 +121,8 @@ class Nodrakorid : DutaMovie() { link.name, link.url, link.referer, - if(link.isM3u8) link.quality else quality ?: Qualities.Unknown.value, - link.isM3u8, + if(link.type == ExtractorLinkType.M3U8) link.quality else quality ?: Qualities.Unknown.value, + link.type, link.headers, link.extractorData ) diff --git a/Hdfilmcehennemi/build.gradle.kts b/Hdfilmcehennemi/build.gradle.kts index 991be08d..3b75abc6 100644 --- a/Hdfilmcehennemi/build.gradle.kts +++ b/Hdfilmcehennemi/build.gradle.kts @@ -1,5 +1,5 @@ // use an integer for version numbers -version = 10 +version = 11 cloudstream { diff --git a/Hdfilmcehennemi/src/main/kotlin/com/hexated/Hdfilmcehennemi.kt b/Hdfilmcehennemi/src/main/kotlin/com/hexated/Hdfilmcehennemi.kt index 9e80daaa..cce3f58e 100644 --- a/Hdfilmcehennemi/src/main/kotlin/com/hexated/Hdfilmcehennemi.kt +++ b/Hdfilmcehennemi/src/main/kotlin/com/hexated/Hdfilmcehennemi.kt @@ -213,7 +213,7 @@ class Hdfilmcehennemi : MainAPI() { link.url, link.referer, link.quality, - link.isM3u8, + link.type, link.headers, link.extractorData ) diff --git a/IdlixProvider/build.gradle.kts b/IdlixProvider/build.gradle.kts index 6533b773..63fef982 100644 --- a/IdlixProvider/build.gradle.kts +++ b/IdlixProvider/build.gradle.kts @@ -1,5 +1,5 @@ // use an integer for version numbers -version = 12 +version = 15 cloudstream { diff --git a/IdlixProvider/src/main/kotlin/com/hexated/IdlixProvider.kt b/IdlixProvider/src/main/kotlin/com/hexated/IdlixProvider.kt index 064c18dd..d103bad9 100644 --- a/IdlixProvider/src/main/kotlin/com/hexated/IdlixProvider.kt +++ b/IdlixProvider/src/main/kotlin/com/hexated/IdlixProvider.kt @@ -5,21 +5,24 @@ import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.LoadResponse.Companion.addActors import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer import com.lagradost.cloudstream3.mvvm.safeApiCall +import com.lagradost.cloudstream3.network.CloudflareKiller import com.lagradost.cloudstream3.utils.* import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson -import com.lagradost.nicehttp.Requests -import com.lagradost.nicehttp.Session +import okhttp3.Interceptor +import okhttp3.Response +import org.jsoup.Jsoup import org.jsoup.nodes.Element import java.net.URI class IdlixProvider : MainAPI() { - override var mainUrl = "https://tv.idlixprime.com" + override var mainUrl = "https://tv.idlixplus.net" private var directUrl = mainUrl override var name = "Idlix" override val hasMainPage = true override var lang = "id" override val hasDownloadSupport = true - private val session = Session(Requests().baseClient) + private val cloudflareKiller by lazy { CloudflareKiller() } + private val interceptor by lazy { CloudflareInterceptor(cloudflareKiller) } override val supportedTypes = setOf( TvType.Movie, TvType.TvSeries, @@ -27,6 +30,8 @@ class IdlixProvider : MainAPI() { TvType.AsianDrama ) + private val key = "\\x5a\\x6d\\x5a\\x6c\\x4e\\x7a\\x55\\x79\\x4d\\x54\\x56\\x6a\\x5a\\x47\\x52\\x69\\x5a\\x44\\x55\\x30\\x5a\\x6d\\x59\\x35\\x4f\\x57\\x45\\x33\\x4d\\x44\\x4a\\x69\\x4e\\x32\\x4a\\x6c\\x4f\\x54\\x42\\x6c\\x4e\\x7a\\x49\\x3d" + override val mainPage = mainPageOf( "$mainUrl/" to "Featured", "$mainUrl/trending/page/?get=movies" to "Trending Movies", @@ -51,9 +56,9 @@ class IdlixProvider : MainAPI() { val url = request.data.split("?") val nonPaged = request.name == "Featured" && page <= 1 val req = if (nonPaged) { - session.get(request.data) + app.get(request.data, interceptor = interceptor) } else { - session.get("${url.first()}$page/?${url.lastOrNull()}") + app.get("${url.first()}$page/?${url.lastOrNull()}", interceptor = interceptor) } mainUrl = getBaseUrl(req.url) val document = req.document @@ -93,12 +98,13 @@ class IdlixProvider : MainAPI() { return newMovieSearchResponse(title, href, TvType.Movie) { this.posterUrl = posterUrl this.quality = quality + posterHeaders = cloudflareKiller.getCookieHeaders(mainUrl).toMap() } } override suspend fun search(query: String): List { - val req = session.get("$mainUrl/search/$query") + val req = app.get("$mainUrl/search/$query", interceptor = interceptor) mainUrl = getBaseUrl(req.url) val document = req.document return document.select("div.result-item").map { @@ -108,12 +114,13 @@ class IdlixProvider : MainAPI() { val posterUrl = it.selectFirst("img")!!.attr("src").toString() newMovieSearchResponse(title, href, TvType.TvSeries) { this.posterUrl = posterUrl + posterHeaders = cloudflareKiller.getCookieHeaders(mainUrl).toMap() } } } override suspend fun load(url: String): LoadResponse { - val request = session.get(url) + val request = app.get(url, interceptor = interceptor, referer = "$directUrl/") directUrl = getBaseUrl(request.url) val document = request.document val title = @@ -142,6 +149,7 @@ class IdlixProvider : MainAPI() { val recPosterUrl = it.selectFirst("img")?.attr("src").toString() newTvSeriesSearchResponse(recName, recHref, TvType.TvSeries) { this.posterUrl = recPosterUrl + posterHeaders = cloudflareKiller.getCookieHeaders(mainUrl).toMap() } } @@ -171,6 +179,7 @@ class IdlixProvider : MainAPI() { addActors(actors) this.recommendations = recommendations addTrailer(trailer) + posterHeaders = cloudflareKiller.getCookieHeaders(mainUrl).toMap() } } else { newMovieLoadResponse(title, url, TvType.Movie, url) { @@ -182,6 +191,7 @@ class IdlixProvider : MainAPI() { addActors(actors) this.recommendations = recommendations addTrailer(trailer) + posterHeaders = cloudflareKiller.getCookieHeaders(mainUrl).toMap() } } } @@ -193,7 +203,7 @@ class IdlixProvider : MainAPI() { callback: (ExtractorLink) -> Unit ): Boolean { - val document = session.get(data).document + val document = app.get(data, interceptor = interceptor, referer = "$directUrl/").document val id = document.select("meta#dooplay-ajax-counter").attr("data-postid") val type = if (data.contains("/movie/")) "movie" else "tv" @@ -201,22 +211,20 @@ class IdlixProvider : MainAPI() { it.attr("data-nume") }.apmap { nume -> safeApiCall { - var source = session.post( - url = "$directUrl/wp-admin/admin-ajax.php", - data = mapOf( - "action" to "doo_player_ajax", - "post" to id, - "nume" to nume, - "type" to type - ), - headers = mapOf("X-Requested-With" to "XMLHttpRequest"), - referer = data - ).let { tryParseJson(it.text) }?.embed_url ?: return@safeApiCall + val source = app.post( + url = "$directUrl/wp-admin/admin-ajax.php", data = mapOf( + "action" to "doo_player_ajax", "post" to id, "nume" to nume, "type" to type + ), headers = mapOf("X-Requested-With" to "XMLHttpRequest"), referer = data, interceptor = interceptor + ).let { tryParseJson(it.text) } ?: return@safeApiCall - if (source.startsWith("https://uservideo.xyz")) { - source = app.get(source).document.select("iframe").attr("src") + val password = if(source.key?.startsWith("\\x") == true) source.key else key + var decrypted = AesHelper.cryptoAESHandler(source.embed_url, password.toByteArray(), false)?.fixBloat() ?: return@safeApiCall + + if (decrypted.startsWith("https://uservideo.xyz")) { + decrypted = app.get(decrypted).document.select("iframe").attr("src") } - loadExtractor(source, directUrl, subtitleCallback, callback) + + getUrl(decrypted, "$directUrl/", subtitleCallback, callback) } } @@ -224,9 +232,86 @@ class IdlixProvider : MainAPI() { return true } + 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 + } + } + + private fun String.fixBloat() : String { + return this.replace("\"", "").replace("\\", "") + } + + private suspend fun getUrl( + url: String, + referer: String?, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ) { + val document = app.get(url, referer = referer).document + val hash = url.split("/").last().substringAfter("data=") + + val m3uLink = app.post( + url = "$mainUrl/player/index.php?data=$hash&do=getVideo", + data = mapOf("hash" to hash, "r" to "$referer"), + referer = referer, + headers = mapOf("X-Requested-With" to "XMLHttpRequest") + ).parsed().videoSource + + M3u8Helper.generateM3u8( + this.name, + m3uLink, + "$referer", + ).forEach(callback) + + + document.select("script").map { script -> + if (script.data().contains("eval(function(p,a,c,k,e,d)")) { + val subData = + getAndUnpack(script.data()).substringAfter("\"tracks\":[").substringBefore("],") + tryParseJson>("[$subData]")?.map { subtitle -> + subtitleCallback.invoke( + SubtitleFile( + getLanguage(subtitle.label ?: ""), + subtitle.file + ) + ) + } + } + } + } + + private fun getLanguage(str: String): String { + return when { + str.contains("indonesia", true) || str + .contains("bahasa", true) -> "Indonesian" + else -> str + } + } + + data class ResponseSource( + @JsonProperty("hls") val hls: Boolean, + @JsonProperty("videoSource") val videoSource: String, + @JsonProperty("securedLink") val securedLink: String?, + ) + + data class Tracks( + @JsonProperty("kind") val kind: String?, + @JsonProperty("file") val file: String, + @JsonProperty("label") val label: String?, + ) + data class ResponseHash( @JsonProperty("embed_url") val embed_url: String, + @JsonProperty("key") val key: String?, @JsonProperty("type") val type: String?, ) + } \ No newline at end of file diff --git a/IdlixProvider/src/main/kotlin/com/hexated/Utils.kt b/IdlixProvider/src/main/kotlin/com/hexated/Utils.kt new file mode 100644 index 00000000..96bb5a14 --- /dev/null +++ b/IdlixProvider/src/main/kotlin/com/hexated/Utils.kt @@ -0,0 +1,95 @@ +package com.hexated + +import com.fasterxml.jackson.annotation.JsonProperty +import com.lagradost.cloudstream3.ErrorLoadingException +import com.lagradost.cloudstream3.base64DecodeArray +import com.lagradost.cloudstream3.base64Encode +import com.lagradost.cloudstream3.utils.AppUtils +import java.security.DigestException +import java.security.MessageDigest +import javax.crypto.Cipher +import javax.crypto.spec.IvParameterSpec +import javax.crypto.spec.SecretKeySpec + +object AesHelper { + + fun cryptoAESHandler( + data: String, + pass: ByteArray, + encrypt: Boolean = true, + padding: String = "AES/CBC/PKCS5PADDING", + ): String? { + val parse = AppUtils.tryParseJson(data) ?: return null + val (key, iv) = generateKeyAndIv(pass, parse.s.hexToByteArray()) ?: throw ErrorLoadingException("failed to generate key") + val cipher = Cipher.getInstance(padding) + return if (!encrypt) { + cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(key, "AES"), IvParameterSpec(iv)) + String(cipher.doFinal(base64DecodeArray(parse.ct))) + } else { + cipher.init(Cipher.ENCRYPT_MODE, SecretKeySpec(key, "AES"), IvParameterSpec(iv)) + base64Encode(cipher.doFinal(parse.ct.toByteArray())) + } + } + + // https://stackoverflow.com/a/41434590/8166854 + private fun generateKeyAndIv( + password: ByteArray, + salt: ByteArray, + hashAlgorithm: String = "MD5", + keyLength: Int = 32, + ivLength: Int = 16, + iterations: Int = 1 + ): List? { + + val md = MessageDigest.getInstance(hashAlgorithm) + val digestLength = md.digestLength + val targetKeySize = keyLength + ivLength + val requiredLength = (targetKeySize + digestLength - 1) / digestLength * digestLength + val generatedData = ByteArray(requiredLength) + var generatedLength = 0 + + try { + md.reset() + + while (generatedLength < targetKeySize) { + if (generatedLength > 0) + md.update( + generatedData, + generatedLength - digestLength, + digestLength + ) + + md.update(password) + md.update(salt, 0, 8) + md.digest(generatedData, generatedLength, digestLength) + + for (i in 1 until iterations) { + md.update(generatedData, generatedLength, digestLength) + md.digest(generatedData, generatedLength, digestLength) + } + + generatedLength += digestLength + } + return listOf( + generatedData.copyOfRange(0, keyLength), + generatedData.copyOfRange(keyLength, targetKeySize) + ) + } catch (e: DigestException) { + return null + } + } + + private fun String.hexToByteArray(): ByteArray { + check(length % 2 == 0) { "Must have an even length" } + return chunked(2) + .map { it.toInt(16).toByte() } + .toByteArray() + } + + private data class AesData( + @JsonProperty("ct") val ct: String, + @JsonProperty("iv") val iv: String, + @JsonProperty("s") val s: String + ) + +} \ No newline at end of file diff --git a/KuronimeProvider/build.gradle.kts b/KuronimeProvider/build.gradle.kts index a4fc0461..74ca1975 100644 --- a/KuronimeProvider/build.gradle.kts +++ b/KuronimeProvider/build.gradle.kts @@ -1,5 +1,5 @@ // use an integer for version numbers -version = 14 +version = 15 cloudstream { diff --git a/KuronimeProvider/src/main/kotlin/com/hexated/KuronimeProvider.kt b/KuronimeProvider/src/main/kotlin/com/hexated/KuronimeProvider.kt index 14a24c6c..9160beb0 100644 --- a/KuronimeProvider/src/main/kotlin/com/hexated/KuronimeProvider.kt +++ b/KuronimeProvider/src/main/kotlin/com/hexated/KuronimeProvider.kt @@ -3,6 +3,7 @@ package com.hexated import com.fasterxml.jackson.annotation.JsonProperty import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer +import com.lagradost.cloudstream3.extractors.helper.AesHelper import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.Qualities @@ -10,12 +11,7 @@ import com.lagradost.cloudstream3.utils.getQualityFromName import com.lagradost.cloudstream3.utils.loadExtractor import org.jsoup.nodes.Element import java.net.URI -import java.security.DigestException -import java.security.MessageDigest import java.util.ArrayList -import javax.crypto.Cipher -import javax.crypto.spec.IvParameterSpec -import javax.crypto.spec.SecretKeySpec class KuronimeProvider : MainAPI() { override var mainUrl = "https://45.12.2.26" @@ -186,10 +182,11 @@ class KuronimeProvider : MainAPI() { argamap( { - val decrypt = cryptoAES( - servers?.src ?: return@argamap, + val decrypt = AesHelper.cryptoAESHandler( + base64Decode(servers?.src ?: return@argamap), KEY.toByteArray(), - false + false, + "AES/CBC/NoPadding" ) val source = tryParseJson(decrypt?.toJsonFormat())?.src?.replace("\\", "") @@ -206,10 +203,11 @@ class KuronimeProvider : MainAPI() { ) }, { - val decrypt = cryptoAES( - servers?.mirror ?: return@argamap, + val decrypt = AesHelper.cryptoAESHandler( + base64Decode(servers?.mirror ?: return@argamap), KEY.toByteArray(), - false + false, + "AES/CBC/NoPadding" ) tryParseJson(decrypt)?.embed?.map { embed -> embed.value.apmap { @@ -249,7 +247,7 @@ class KuronimeProvider : MainAPI() { link.url, link.referer, getQualityFromName(quality), - link.isM3u8, + link.type, link.headers, link.extractorData ) @@ -263,86 +261,6 @@ class KuronimeProvider : MainAPI() { } } - // https://stackoverflow.com/a/41434590/8166854 - private fun generateKeyAndIv( - password: ByteArray, - salt: ByteArray, - hashAlgorithm: String = "MD5", - keyLength: Int = 32, - ivLength: Int = 16, - iterations: Int = 1 - ): List? { - - val md = MessageDigest.getInstance(hashAlgorithm) - val digestLength = md.digestLength - val targetKeySize = keyLength + ivLength - val requiredLength = (targetKeySize + digestLength - 1) / digestLength * digestLength - val generatedData = ByteArray(requiredLength) - var generatedLength = 0 - - try { - md.reset() - - while (generatedLength < targetKeySize) { - if (generatedLength > 0) - md.update( - generatedData, - generatedLength - digestLength, - digestLength - ) - - md.update(password) - md.update(salt, 0, 8) - md.digest(generatedData, generatedLength, digestLength) - - for (i in 1 until iterations) { - md.update(generatedData, generatedLength, digestLength) - md.digest(generatedData, generatedLength, digestLength) - } - - generatedLength += digestLength - } - return listOf( - generatedData.copyOfRange(0, keyLength), - generatedData.copyOfRange(keyLength, targetKeySize) - ) - } catch (e: DigestException) { - return null - } - } - - private fun String.decodeHex(): ByteArray { - check(length % 2 == 0) { "Must have an even length" } - return chunked(2) - .map { it.toInt(16).toByte() } - .toByteArray() - } - - private fun cryptoAES( - data: String, - pass: ByteArray, - encrypt: Boolean = true - ): String? { - val json = tryParseJson(base64Decode(data)) - ?: throw ErrorLoadingException("No Data Found") - val (key, iv) = generateKeyAndIv(pass, json.s.decodeHex()) ?: return null - val cipher = Cipher.getInstance("AES/CBC/NoPadding") - return if (!encrypt) { - cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(key, "AES"), IvParameterSpec(iv)) - String(cipher.doFinal(base64DecodeArray(json.ct))) - } else { - cipher.init(Cipher.ENCRYPT_MODE, SecretKeySpec(key, "AES"), IvParameterSpec(iv)) - base64Encode(cipher.doFinal(json.ct.toByteArray())) - - } - } - - data class AesData( - @JsonProperty("ct") val ct: String, - @JsonProperty("iv") val iv: String, - @JsonProperty("s") val s: String - ) - data class Mirrors( @JsonProperty("embed") val embed: Map> = emptyMap(), ) diff --git a/LayarKacaProvider/build.gradle.kts b/LayarKacaProvider/build.gradle.kts index 7ec4087a..0be264f6 100644 --- a/LayarKacaProvider/build.gradle.kts +++ b/LayarKacaProvider/build.gradle.kts @@ -1,5 +1,5 @@ // use an integer for version numbers -version = 15 +version = 16 cloudstream { diff --git a/LayarKacaProvider/src/main/kotlin/com/hexated/LayarKacaProvider.kt b/LayarKacaProvider/src/main/kotlin/com/hexated/LayarKacaProvider.kt index a4055e54..cbccb55d 100644 --- a/LayarKacaProvider/src/main/kotlin/com/hexated/LayarKacaProvider.kt +++ b/LayarKacaProvider/src/main/kotlin/com/hexated/LayarKacaProvider.kt @@ -9,7 +9,7 @@ import org.jsoup.nodes.Element import java.net.URLDecoder class LayarKacaProvider : MainAPI() { - override var mainUrl = "https://tv1.lk21official.pro" + override var mainUrl = "https://tv3.lk21official.pro" private var seriesUrl = "https://tv1.nontondrama.click" override var name = "LayarKaca" override val hasMainPage = true @@ -72,11 +72,11 @@ class LayarKacaProvider : MainAPI() { } override suspend fun search(query: String): List { - val document = app.get("$mainUrl/?s=$query").document - return document.select("div.search-item").map { - val title = it.selectFirst("h2 > a")!!.text().trim() - val href = fixUrl(it.selectFirst("a")!!.attr("href")) - val posterUrl = fixUrl(it.selectFirst("img.img-thumbnail")?.attr("src").toString()) + val document = app.get("$mainUrl/search.php?s=$query").document + return document.select("div.search-item").mapNotNull { + val title = it.selectFirst("a")?.attr("title") ?: "" + val href = fixUrl(it.selectFirst("a")?.attr("href") ?: return@mapNotNull null) + val posterUrl = fixUrlNull(it.selectFirst("img.img-thumbnail")?.attr("src")) newTvSeriesSearchResponse(title, href, TvType.TvSeries) { this.posterUrl = posterUrl } @@ -172,8 +172,6 @@ class LayarKacaProvider : MainAPI() { return app.get(this, referer = "$seriesUrl/").document.select("div.embed iframe").attr("src") } - private fun decode(input: String): String = URLDecoder.decode(input, "utf-8").replace(" ", "%20") - } open class Emturbovid : ExtractorApi() { diff --git a/Minioppai/build.gradle.kts b/Minioppai/build.gradle.kts index 368ffe23..ee49ee06 100644 --- a/Minioppai/build.gradle.kts +++ b/Minioppai/build.gradle.kts @@ -1,5 +1,5 @@ // use an integer for version numbers -version = 6 +version = 8 cloudstream { diff --git a/Minioppai/src/main/kotlin/com/hexated/Extractors.kt b/Minioppai/src/main/kotlin/com/hexated/Extractors.kt index 95b96d9e..6a82e5d3 100644 --- a/Minioppai/src/main/kotlin/com/hexated/Extractors.kt +++ b/Minioppai/src/main/kotlin/com/hexated/Extractors.kt @@ -16,6 +16,11 @@ class Paistream : Streampai() { override val mainUrl = "https://paistream.my.id" } +class TvMinioppai : Streampai() { + override val name = "Minioppai" + override val mainUrl = "https://tv.minioppai.org" +} + open class Streampai : ExtractorApi() { override val name = "Streampai" override val mainUrl = "https://streampai.my.id" diff --git a/Minioppai/src/main/kotlin/com/hexated/Minioppai.kt b/Minioppai/src/main/kotlin/com/hexated/Minioppai.kt index 0a263379..2dc220a1 100644 --- a/Minioppai/src/main/kotlin/com/hexated/Minioppai.kt +++ b/Minioppai/src/main/kotlin/com/hexated/Minioppai.kt @@ -47,7 +47,7 @@ class Minioppai : MainAPI() { override val mainPage = mainPageOf( "$mainUrl/watch" to "New Episode", - "$mainUrl/popular" to "Popular Hentai", + "$mainUrl/populars" to "Popular Hentai", ) override suspend fun getMainPage( diff --git a/Minioppai/src/main/kotlin/com/hexated/MinioppaiPlugin.kt b/Minioppai/src/main/kotlin/com/hexated/MinioppaiPlugin.kt index 83fbd781..5832d055 100644 --- a/Minioppai/src/main/kotlin/com/hexated/MinioppaiPlugin.kt +++ b/Minioppai/src/main/kotlin/com/hexated/MinioppaiPlugin.kt @@ -11,5 +11,6 @@ class MinioppaiPlugin: Plugin() { registerMainAPI(Minioppai()) registerExtractorAPI(Streampai()) registerExtractorAPI(Paistream()) + registerExtractorAPI(TvMinioppai()) } } \ No newline at end of file diff --git a/Movierulzhd/src/main/kotlin/com/hexated/Hdmovie2.kt b/Movierulzhd/src/main/kotlin/com/hexated/Hdmovie2.kt index f47e57b1..63831c0e 100644 --- a/Movierulzhd/src/main/kotlin/com/hexated/Hdmovie2.kt +++ b/Movierulzhd/src/main/kotlin/com/hexated/Hdmovie2.kt @@ -11,7 +11,9 @@ import com.lagradost.cloudstream3.utils.loadExtractor import org.jsoup.Jsoup class Hdmovie2 : Movierulzhd() { + override var mainUrl = "https://hdmovie2.codes" + override var name = "Hdmovie2" override val mainPage = mainPageOf( diff --git a/Movierulzhd/src/main/kotlin/com/hexated/Movierulzhd.kt b/Movierulzhd/src/main/kotlin/com/hexated/Movierulzhd.kt index ae47ccbf..9c5c93af 100644 --- a/Movierulzhd/src/main/kotlin/com/hexated/Movierulzhd.kt +++ b/Movierulzhd/src/main/kotlin/com/hexated/Movierulzhd.kt @@ -11,7 +11,9 @@ import org.jsoup.nodes.Element import java.net.URI open class Movierulzhd : MainAPI() { + override var mainUrl = "https://movierulzvid.gold" + var directUrl = "" override var name = "Movierulzhd" override val hasMainPage = true diff --git a/MultiplexProvider/build.gradle.kts b/MultiplexProvider/build.gradle.kts deleted file mode 100644 index 51ca5d94..00000000 --- a/MultiplexProvider/build.gradle.kts +++ /dev/null @@ -1,27 +0,0 @@ -// use an integer for version numbers -version = 2 - - -cloudstream { - language = "id" - // All of these properties are optional, you can safely remove them - - // description = "Lorem Ipsum" - authors = listOf("Hexated") - - /** - * Status int as the following: - * 0: Down - * 1: Ok - * 2: Slow - * 3: Beta only - * */ - status = 1 // will be 3 if unspecified - tvTypes = listOf( - "AsianDrama", - "TvSeries", - "Movie", - ) - - iconUrl = "https://www.google.com/s2/favicons?domain=146.19.24.137&sz=%size%" -} \ No newline at end of file diff --git a/MultiplexProvider/src/main/AndroidManifest.xml b/MultiplexProvider/src/main/AndroidManifest.xml deleted file mode 100644 index c98063f8..00000000 --- a/MultiplexProvider/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/MultiplexProvider/src/main/kotlin/com/hexated/MultiplexProvider.kt b/MultiplexProvider/src/main/kotlin/com/hexated/MultiplexProvider.kt deleted file mode 100644 index 7a76be91..00000000 --- a/MultiplexProvider/src/main/kotlin/com/hexated/MultiplexProvider.kt +++ /dev/null @@ -1,188 +0,0 @@ -package com.hexated - -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.utils.AppUtils.tryParseJson -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.getQualityFromName -import org.jsoup.nodes.Element - -class MultiplexProvider : MainAPI() { - override var mainUrl = "http://5.104.81.46" - override var name = "Multiplex" - override val hasMainPage = true - override var lang = "id" - override val hasDownloadSupport = true - override val supportedTypes = setOf( - TvType.Movie, - TvType.TvSeries, - TvType.AsianDrama - ) - - override val mainPage = mainPageOf( - "$mainUrl/genre/top-popular-movies/page/" to "Top Popolar Movies", - "$mainUrl/genre/series-ongoing/page/" to "Series Ongoing", - "$mainUrl/genre/series-barat/page/" to "Series Barat", - "$mainUrl/genre/series-korea/page/" to "Series Korea", - ) - - override suspend fun getMainPage( - page: Int, - request: MainPageRequest - ): HomePageResponse { - val document = app.get(request.data + page).document - val home = document.select("article.item").mapNotNull { - it.toSearchResult() - } - return newHomePageResponse(request.name, home) - } - - 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")?.attr("data-src")) - val quality = this.select("div.gmr-quality-item > a").text().trim() - return if (quality.isEmpty()) { - val episode = this.select("div.gmr-numbeps > span").text().toIntOrNull() - newAnimeSearchResponse(title, href, TvType.TvSeries) { - this.posterUrl = posterUrl - addSub(episode) - } - } else { - newMovieSearchResponse(title, href, TvType.Movie) { - this.posterUrl = posterUrl - addQuality(quality) - } - } - } - - private fun Element.toBottomSearchResult(): SearchResponse? { - val title = this.selectFirst("a > span.idmuvi-rp-title")?.text()?.trim() ?: return null - val href = this.selectFirst("a")!!.attr("href") - val posterUrl = fixUrl(this.selectFirst("a > img")?.attr("data-src").toString()) - return newMovieSearchResponse(title, href, TvType.Movie) { - this.posterUrl = posterUrl - } - } - - override suspend fun search(query: String): List { - val link = "$mainUrl/?s=$query&post_type[]=post&post_type[]=tv" - val document = app.get(link).document - return document.select("article.item").mapNotNull { - it.toSearchResult() - } - } - - override suspend fun load(url: String): LoadResponse { - val document = app.get(url).document - - val title = - document.selectFirst("h1.entry-title")?.text()?.substringBefore("Season")?.trim() - .toString() - val poster = - fixUrl(document.selectFirst("figure.pull-left > img")?.attr("data-src").toString()) - val tags = document.select("span.gmr-movie-genre:contains(Genre:) > a").map { it.text() } - - val year = - document.select("span.gmr-movie-genre:contains(Year:) > a").text().trim().toIntOrNull() - val tvType = if (url.contains("/tv/")) TvType.TvSeries else TvType.Movie - val description = document.selectFirst("div[itemprop=description] > p")?.text()?.trim() - val trailer = document.selectFirst("ul.gmr-player-nav li a.gmr-trailer-popup")?.attr("href") - val rating = - document.selectFirst("div.gmr-meta-rating > span[itemprop=ratingValue]")?.text() - ?.toRatingInt() - val actors = document.select("div.gmr-moviedata").last()?.select("span[itemprop=actors]") - ?.map { it.select("a").text() } - - val recommendations = document.select("div.idmuvi-rp ul li").mapNotNull { - it.toBottomSearchResult() - } - - return if (tvType == TvType.TvSeries) { - val episodes = document.select("div.gmr-listseries > a").map { - val href = fixUrl(it.attr("href")) - val episode = it.text().split(" ").last().toIntOrNull() - val season = it.text().split(" ").first().substringAfter("S").toIntOrNull() - Episode( - href, - "Episode $episode", - season, - episode, - ) - } - newTvSeriesLoadResponse(title, url, TvType.TvSeries, episodes) { - this.posterUrl = poster - this.year = year - this.plot = description - this.tags = tags - this.rating = rating - addActors(actors) - this.recommendations = recommendations - addTrailer(trailer) - } - } else { - newMovieLoadResponse(title, url, TvType.Movie, url) { - this.posterUrl = poster - this.year = year - this.plot = description - this.tags = tags - this.rating = rating - addActors(actors) - this.recommendations = recommendations - addTrailer(trailer) - } - } - } - - private data class ResponseSource( - @JsonProperty("file") val file: String, - @JsonProperty("type") val type: String?, - @JsonProperty("label") val label: String? - ) - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - - val document = app.get(data).document - - val id = document.selectFirst("div#muvipro_player_content_id")!!.attr("data-id") - val server = app.post( - "$mainUrl/wp-admin/admin-ajax.php", - data = mapOf("action" to "muvipro_player_content", "tab" to "player1", "post_id" to id) - ).document.select("iframe").attr("src") - - app.get(server, referer = "$mainUrl/").document.select("script").map { script -> - if (script.data().contains("var config = {")) { - val source = script.data().substringAfter("sources: [").substringBefore("],") - tryParseJson>("[$source]")?.map { m3u -> - val m3uData = app.get(m3u.file, referer = "https://gdriveplayer.link/").text - val quality = - Regex("\\d{3,4}\\.m3u8").findAll(m3uData).map { it.value }.toList() - quality.forEach { - callback.invoke( - ExtractorLink( - source = name, - name = name, - url = m3u.file.replace("video.m3u8", it), - referer = "https://gdriveplayer.link/", - quality = getQualityFromName("${it.replace(".m3u8", "")}p"), - isM3u8 = true - ) - ) - } - } - } - } - - return true - - } - - -} \ No newline at end of file diff --git a/MultiplexProvider/src/main/kotlin/com/hexated/MultiplexProviderPlugin.kt b/MultiplexProvider/src/main/kotlin/com/hexated/MultiplexProviderPlugin.kt deleted file mode 100644 index f689b83f..00000000 --- a/MultiplexProvider/src/main/kotlin/com/hexated/MultiplexProviderPlugin.kt +++ /dev/null @@ -1,14 +0,0 @@ - -package com.hexated - -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin -import android.content.Context - -@CloudstreamPlugin -class MultiplexProviderPlugin: Plugin() { - override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(MultiplexProvider()) - } -} \ No newline at end of file diff --git a/Nekopoi/build.gradle.kts b/Nekopoi/build.gradle.kts index 0d3c4901..809c2b60 100644 --- a/Nekopoi/build.gradle.kts +++ b/Nekopoi/build.gradle.kts @@ -1,5 +1,5 @@ // use an integer for version numbers -version = 5 +version = 6 cloudstream { diff --git a/Nekopoi/src/main/kotlin/com/hexated/Nekopoi.kt b/Nekopoi/src/main/kotlin/com/hexated/Nekopoi.kt index c9a00826..124595b6 100644 --- a/Nekopoi/src/main/kotlin/com/hexated/Nekopoi.kt +++ b/Nekopoi/src/main/kotlin/com/hexated/Nekopoi.kt @@ -170,8 +170,8 @@ class Nekopoi : MainAPI() { link.name, link.url, link.referer, - if (link.isM3u8) link.quality else it.first, - link.isM3u8, + if (link.type == ExtractorLinkType.M3U8) link.quality else it.first, + link.type, link.headers, link.extractorData ) diff --git a/Nimegami/build.gradle.kts b/Nimegami/build.gradle.kts index f4617aee..afc730a5 100644 --- a/Nimegami/build.gradle.kts +++ b/Nimegami/build.gradle.kts @@ -1,5 +1,5 @@ // use an integer for version numbers -version = 3 +version = 4 cloudstream { diff --git a/Nimegami/src/main/kotlin/com/hexated/Nimegami.kt b/Nimegami/src/main/kotlin/com/hexated/Nimegami.kt index a2ed9aaf..3143bc15 100644 --- a/Nimegami/src/main/kotlin/com/hexated/Nimegami.kt +++ b/Nimegami/src/main/kotlin/com/hexated/Nimegami.kt @@ -164,7 +164,7 @@ class Nimegami : MainAPI() { link.url, link.referer, getQualityFromName(quality), - link.isM3u8, + link.type, link.headers, link.extractorData ) diff --git a/OploverzProvider/build.gradle.kts b/OploverzProvider/build.gradle.kts index 036ff106..3b5d9597 100644 --- a/OploverzProvider/build.gradle.kts +++ b/OploverzProvider/build.gradle.kts @@ -1,5 +1,5 @@ // use an integer for version numbers -version = 21 +version = 23 cloudstream { diff --git a/OploverzProvider/src/main/kotlin/com/hexated/Extractors.kt b/OploverzProvider/src/main/kotlin/com/hexated/Extractors.kt new file mode 100644 index 00000000..c38c13b8 --- /dev/null +++ b/OploverzProvider/src/main/kotlin/com/hexated/Extractors.kt @@ -0,0 +1,45 @@ +package com.hexated + +import com.lagradost.cloudstream3.SubtitleFile +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.utils.* + +open class Qiwi : ExtractorApi() { + override val name = "Qiwi" + override val mainUrl = "https://qiwi.gg" + override val requiresReferer = true + + override suspend fun getUrl( + url: String, + referer: String?, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ) { + val document = app.get(url, referer = referer).document + val title = document.select("title").text() + val source = document.select("video source").attr("src") + + callback.invoke( + ExtractorLink( + this.name, + this.name, + source, + "$mainUrl/", + getIndexQuality(title), + headers = mapOf( + "Accept" to "video/webm,video/ogg,video/*;q=0.9,application/ogg;q=0.7,audio/*;q=0.6,*/*;q=0.5", + "Range" to "bytes=0-", + "Sec-Fetch-Dest" to "video", + "Sec-Fetch-Mode" to "no-cors", + ) + ) + ) + + } + + private fun getIndexQuality(str: String): Int { + return Regex("(\\d{3,4})[pP]").find(str)?.groupValues?.getOrNull(1)?.toIntOrNull() + ?: Qualities.Unknown.value + } + +} \ No newline at end of file diff --git a/OploverzProvider/src/main/kotlin/com/hexated/OploverzProvider.kt b/OploverzProvider/src/main/kotlin/com/hexated/OploverzProvider.kt index 26cac833..92547017 100644 --- a/OploverzProvider/src/main/kotlin/com/hexated/OploverzProvider.kt +++ b/OploverzProvider/src/main/kotlin/com/hexated/OploverzProvider.kt @@ -6,7 +6,7 @@ import com.lagradost.cloudstream3.utils.* import org.jsoup.nodes.Element class OploverzProvider : MainAPI() { - override var mainUrl = "https://oploverz.team" + override var mainUrl = "https://oploverz.red" override var name = "Oploverz" override val hasMainPage = true override var lang = "id" @@ -195,7 +195,7 @@ class OploverzProvider : MainAPI() { link.url, link.referer, name.fixQuality(), - link.isM3u8, + link.type, link.headers, link.extractorData ) diff --git a/OploverzProvider/src/main/kotlin/com/hexated/OploverzProviderPlugin.kt b/OploverzProvider/src/main/kotlin/com/hexated/OploverzProviderPlugin.kt index 157869bd..ba7c0b85 100644 --- a/OploverzProvider/src/main/kotlin/com/hexated/OploverzProviderPlugin.kt +++ b/OploverzProvider/src/main/kotlin/com/hexated/OploverzProviderPlugin.kt @@ -10,5 +10,6 @@ class OploverzProviderPlugin: Plugin() { override fun load(context: Context) { // All providers should be added in this manner. Please don't edit the providers list directly. registerMainAPI(OploverzProvider()) + registerExtractorAPI(Qiwi()) } } \ No newline at end of file diff --git a/OtakudesuProvider/build.gradle.kts b/OtakudesuProvider/build.gradle.kts index 2f4a7f48..39e00b68 100644 --- a/OtakudesuProvider/build.gradle.kts +++ b/OtakudesuProvider/build.gradle.kts @@ -1,5 +1,5 @@ // use an integer for version numbers -version = 13 +version = 14 cloudstream { diff --git a/OtakudesuProvider/src/main/kotlin/com/hexated/OtakudesuProvider.kt b/OtakudesuProvider/src/main/kotlin/com/hexated/OtakudesuProvider.kt index 3bc73926..0a4b0dd3 100644 --- a/OtakudesuProvider/src/main/kotlin/com/hexated/OtakudesuProvider.kt +++ b/OtakudesuProvider/src/main/kotlin/com/hexated/OtakudesuProvider.kt @@ -236,7 +236,7 @@ class OtakudesuProvider : MainAPI() { link.url, link.referer, quality, - link.isM3u8, + link.type, link.headers, link.extractorData ) diff --git a/PhimmoichillProvider/src/main/kotlin/com/hexated/PhimmoichillProvider.kt b/PhimmoichillProvider/src/main/kotlin/com/hexated/PhimmoichillProvider.kt index 79823186..aa7c4f5d 100644 --- a/PhimmoichillProvider/src/main/kotlin/com/hexated/PhimmoichillProvider.kt +++ b/PhimmoichillProvider/src/main/kotlin/com/hexated/PhimmoichillProvider.kt @@ -9,7 +9,7 @@ import org.jsoup.nodes.Element import java.net.URLDecoder class PhimmoichillProvider : MainAPI() { - override var mainUrl = "https://phimmoichilld.net" + override var mainUrl = "https://phimmoichillg.net" override var name = "Phimmoichill" override val hasMainPage = true override var lang = "vi" diff --git a/Samehadaku/build.gradle.kts b/Samehadaku/build.gradle.kts index c7f120a3..1c566566 100644 --- a/Samehadaku/build.gradle.kts +++ b/Samehadaku/build.gradle.kts @@ -1,5 +1,5 @@ // use an integer for version numbers -version = 13 +version = 14 cloudstream { diff --git a/Samehadaku/src/main/kotlin/com/hexated/Samehadaku.kt b/Samehadaku/src/main/kotlin/com/hexated/Samehadaku.kt index 78264113..a903d225 100644 --- a/Samehadaku/src/main/kotlin/com/hexated/Samehadaku.kt +++ b/Samehadaku/src/main/kotlin/com/hexated/Samehadaku.kt @@ -203,7 +203,7 @@ class Samehadaku : MainAPI() { link.url, link.referer, name.fixQuality(), - link.isM3u8, + link.type, link.headers, link.extractorData ) diff --git a/SoraStream/build.gradle.kts b/SoraStream/build.gradle.kts index a9ef66fb..10dc59bc 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 = 159 +version = 164 android { defaultConfig { diff --git a/SoraStream/src/main/kotlin/com/hexated/Extractors.kt b/SoraStream/src/main/kotlin/com/hexated/Extractors.kt new file mode 100644 index 00000000..6def3d06 --- /dev/null +++ b/SoraStream/src/main/kotlin/com/hexated/Extractors.kt @@ -0,0 +1,162 @@ +package com.hexated + +import com.lagradost.cloudstream3.extractors.Filesim +import com.lagradost.cloudstream3.extractors.GMPlayer +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.SubtitleFile +import com.lagradost.cloudstream3.app +import com.lagradost.cloudstream3.utils.* +import java.math.BigInteger +import java.security.MessageDigest + +open class Playm4u : ExtractorApi() { + override val name = "Playm4u" + override val mainUrl = "https://play9str.playm4u.xyz" + override val requiresReferer = true + private val password = "plhq@@@22" + + override suspend fun getUrl( + url: String, + referer: String?, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ) { + val document = app.get(url, referer = referer).document + val script = document.selectFirst("script:containsData(idfile =)")?.data() ?: return + val passScript = document.selectFirst("script:containsData(domain_ref =)")?.data() ?: return + + val pass = passScript.substringAfter("CryptoJS.MD5('").substringBefore("')") + val amount = passScript.substringAfter(".toString()), ").substringBefore("));").toInt() + + val idFile = "idfile\\s*=\\s*[\"'](\\S+)[\"'];".findIn(script) + val idUser = "idUser\\s*=\\s*[\"'](\\S+)[\"'];".findIn(script) + val domainApi = "DOMAIN_API\\s*=\\s*[\"'](\\S+)[\"'];".findIn(script) + val nameKeyV3 = "NameKeyV3\\s*=\\s*[\"'](\\S+)[\"'];".findIn(script) + val dataEnc = caesarShift( + mahoa( + "Win32|$idUser|$idFile|$referer", + md5(pass) + ), amount + ).toHex() + + val captchaKey = + document.select("script[src*=https://www.google.com/recaptcha/api.js?render=]") + .attr("src").substringAfter("render=") + val token = getCaptchaToken( + url, + captchaKey, + referer = referer + ) + + val source = app.post( + domainApi, data = mapOf( + "namekey" to nameKeyV3, + "token" to "$token", + "referrer" to "$referer", + "data" to "$dataEnc|${md5(dataEnc + password)}", + ), referer = "$mainUrl/" + ).parsedSafe() + + callback.invoke( + ExtractorLink( + this.name, + this.name, + source?.data ?: return, + "$mainUrl/", + Qualities.P1080.value, + INFER_TYPE + ) + ) + + subtitleCallback.invoke( + SubtitleFile( + source.sub?.substringBefore("|")?.toLanguage() ?: return, + source.sub.substringAfter("|"), + ) + ) + + } + + private fun caesarShift(str: String, amount: Int): String { + var output = "" + val adjustedAmount = if (amount < 0) amount + 26 else amount + for (element in str) { + var c = element + if (c.isLetter()) { + val code = c.code + c = when (code) { + in 65..90 -> ((code - 65 + adjustedAmount) % 26 + 65).toChar() + in 97..122 -> ((code - 97 + adjustedAmount) % 26 + 97).toChar() + else -> c + } + } + output += c + } + return output + } + + private fun mahoa(input: String, key: String): String { + val a = CryptoJS.encrypt(key, input) + return a.replace("U2FsdGVkX1", "") + .replace("/", "|a") + .replace("+", "|b") + .replace("=", "|c") + .replace("|", "-z") + } + + private fun md5(input: String): String { + val md = MessageDigest.getInstance("MD5") + return BigInteger(1, md.digest(input.toByteArray())).toString(16).padStart(32, '0') + } + + private fun String.toHex(): String { + return this.toByteArray().joinToString("") { "%02x".format(it) } + } + + private fun String.findIn(data: String): String { + return this.toRegex().find(data)?.groupValues?.get(1) ?: "" + } + + private fun String.toLanguage() : String { + return if(this == "EN") "English" else this + } + + data class Source( + @JsonProperty("data") val data: String? = null, + @JsonProperty("sub") val sub: String? = null, + ) + +} + +class TravelR : GMPlayer() { + override val name = "TravelR" + override val mainUrl = "https://travel-russia.xyz" +} + +class Mwish : Filesim() { + override val name = "Mwish" + override var mainUrl = "https://mwish.pro" +} + +class Animefever : Filesim() { + override val name = "Animefever" + override var mainUrl = "https://animefever.fun" +} + +class Multimovies : Filesim() { + override val name = "Multimovies" + override var mainUrl = "https://multimovies.cloud" +} + +class MultimoviesSB : StreamSB() { + override var name = "Multimovies" + override var mainUrl = "https://multimovies.website" +} + +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 5e131676..3f53a400 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt @@ -9,6 +9,7 @@ import com.lagradost.cloudstream3.extractors.Filesim import com.lagradost.cloudstream3.extractors.GMPlayer import com.lagradost.cloudstream3.extractors.StreamSB import com.lagradost.cloudstream3.extractors.Voe +import com.lagradost.cloudstream3.extractors.helper.AesHelper.cryptoAESHandler import com.lagradost.cloudstream3.extractors.helper.GogoHelper import com.lagradost.cloudstream3.network.CloudflareKiller import com.lagradost.nicehttp.RequestBodyTypes @@ -126,7 +127,7 @@ object SoraExtractor : SoraStream() { link.url, link.referer, if (link.name == "VidSrc") Qualities.P1080.value else link.quality, - link.isM3u8, + link.type, link.headers, link.extractorData ) @@ -272,7 +273,7 @@ object SoraExtractor : SoraStream() { video.url, video.referer, Qualities.P1080.value, - video.isM3u8, + video.type, video.headers, video.extractorData ) @@ -414,7 +415,7 @@ object SoraExtractor : SoraStream() { } else { "$idlixAPI/episode/$fixTitle-season-$season-episode-$episode" } - invokeWpmovies(url, subtitleCallback, callback) + invokeWpmovies(url, subtitleCallback, callback, encrypt = true) } suspend fun invokeMultimovies( @@ -455,8 +456,14 @@ object SoraExtractor : SoraStream() { subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit, fixIframe: Boolean = false, + encrypt: Boolean = false, + key: String? = null, ) { - val res = session.get(url ?: return) + fun String.fixBloat() : String { + return this.replace("\"", "").replace("\\", "") + } + val res = app.get(url ?: return) + val headers = mapOf("X-Requested-With" to "XMLHttpRequest") val referer = getBaseUrl(res.url) val document = res.document document.select("ul#playeroptionsul > li").map { @@ -466,13 +473,17 @@ object SoraExtractor : SoraStream() { it.attr("data-type") ) }.apmap { (id, nume, type) -> - val json = session.post( + val json = app.post( url = "$referer/wp-admin/admin-ajax.php", data = mapOf( "action" to "doo_player_ajax", "post" to id, "nume" to nume, "type" to type - ), headers = mapOf("X-Requested-With" to "XMLHttpRequest"), referer = url + ), headers = headers, referer = url ) - val source = tryParseJson(json.text)?.embed_url?.let { - if (fixIframe) Jsoup.parse(it).select("IFRAME").attr("SRC") else it + val source = tryParseJson(json.text)?.let { + when { + encrypt -> cryptoAESHandler(it.embed_url,(it.key ?: return@apmap).toByteArray(), false)?.fixBloat() + fixIframe -> Jsoup.parse(it.embed_url).select("IFRAME").attr("SRC") + else -> it.embed_url + } } ?: return@apmap if (!source.contains("youtube")) { loadExtractor(source, "$referer/", subtitleCallback, callback) @@ -700,10 +711,10 @@ object SoraExtractor : SoraStream() { link.url, link.referer, when { - link.isM3u8 -> link.quality + link.type == ExtractorLinkType.M3U8 -> link.quality else -> getQualityFromName(it.first) }, - link.isM3u8, + link.type, link.headers, link.extractorData ) @@ -1028,10 +1039,10 @@ object SoraExtractor : SoraStream() { link.url, link.referer, when { - link.isM3u8 -> link.quality + link.type == ExtractorLinkType.M3U8 -> link.quality else -> it.third }, - link.isM3u8, + link.type, link.headers, link.extractorData ) @@ -1506,7 +1517,9 @@ object SoraExtractor : SoraStream() { subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit ) { - val res = app.get("$m4uhdAPI/search/${title.createSlug()}.html").document + val req = app.get("$m4uhdAPI/search/${title.createSlug()}.html") + val referer = getBaseUrl(req.url) + val res = req.document val scriptData = res.select("div.row div.item").map { Triple( it.selectFirst("img.imagecover")?.attr("title"), @@ -1527,7 +1540,7 @@ object SoraExtractor : SoraStream() { } } - val link = fixUrl(script?.third ?: return, m4uhdAPI) + val link = fixUrl(script?.third ?: return, referer) val request = app.get(link) var cookiesSet = request.headers.filter { it.first == "set-cookie" } var xsrf = @@ -1547,7 +1560,7 @@ object SoraExtractor : SoraStream() { ?: return val idepisode = episodeData.select("button").attr("idepisode") ?: return val requestEmbed = app.post( - "$m4uhdAPI/ajaxtv", data = mapOf( + "$referer/ajaxtv", data = mapOf( "idepisode" to idepisode, "_token" to "$token" ), referer = link, headers = mapOf( "X-Requested-With" to "XMLHttpRequest", @@ -1561,14 +1574,16 @@ object SoraExtractor : SoraStream() { cookiesSet.find { it.second.contains("XSRF-TOKEN") }?.second?.substringAfter("XSRF-TOKEN=") ?.substringBefore(";") session = - cookiesSet.find { it.second.contains("laravel_session") }?.second?.substringAfter("laravel_session=") + cookiesSet.find { it.second.contains("laravel_session") }?.second?.substringAfter( + "laravel_session=" + ) ?.substringBefore(";") requestEmbed.document.select("div.le-server span").map { it.attr("data") } } m4uData.apmap { data -> val iframe = app.post( - "$m4uhdAPI/ajax", + "$referer/ajax", data = mapOf( "m4u" to data, "_token" to "$token" ), @@ -1583,7 +1598,7 @@ object SoraExtractor : SoraStream() { ), ).document.select("iframe").attr("src") - loadExtractor(iframe, m4uhdAPI, subtitleCallback, callback) + loadExtractor(iframe, referer, subtitleCallback, callback) } } @@ -2540,80 +2555,6 @@ object SoraExtractor : SoraStream() { } - suspend fun invokePutlocker( - title: String? = null, - year: Int? = null, - season: Int? = null, - episode: Int? = null, - callback: (ExtractorLink) -> Unit, - ) { - val query = if (season == null) { - title - } else { - "$title - season $season" - } - - val res = app.get("$putlockerAPI/movie/search/$query").document - val scripData = res.select("div.movies-list div.ml-item").map { - it.selectFirst("h2")?.text() to it.selectFirst("a")?.attr("href") - } - val script = if (scripData.size == 1) { - scripData.first() - } else { - scripData.find { - if (season == null) { - it.first.equals(title, true) || (it.first?.contains( - "$title", true - ) == true && it.first?.contains("$year") == true) - } else { - it.first?.contains("$title", true) == true && it.first?.contains( - "Season $season", true - ) == true - } - } - } - - val id = fixUrl(script?.second ?: return).split("-").lastOrNull()?.removeSuffix("/") - val iframe = app.get("$putlockerAPI/ajax/movie_episodes/$id") - .parsedSafe()?.html?.let { Jsoup.parse(it) }?.let { server -> - if (season == null) { - server.select("div.les-content a").map { - it.attr("data-id") to it.attr("data-server") - } - } else { - server.select("div.les-content a").map { it } - .filter { it.text().contains("Episode $episode", true) }.map { - it.attr("data-id") to it.attr("data-server") - } - } - } - - iframe?.apmap { - delay(3000) - val embedUrl = app.get("$putlockerAPI/ajax/movie_embed/${it.first}") - .parsedSafe()?.src ?: return@apmap null - val sources = extractPutlockerSources(embedUrl)?.parsedSafe() - - argamap( - { - sources?.callback(embedUrl, "Server ${it.second}", callback) - }, - { - if (!sources?.backupLink.isNullOrBlank()) { - extractPutlockerSources(sources?.backupLink)?.parsedSafe() - ?.callback( - embedUrl, "Backup ${it.second}", callback - ) - } else { - return@argamap - } - }, - ) - - } - - } - suspend fun invokeCryMovies( imdbId: String? = null, title: String? = null, @@ -2884,36 +2825,84 @@ object SoraExtractor : SoraStream() { } + suspend fun invokeSusflix( + tmdbId: Int? = null, + season: Int? = null, + episode: Int? = null, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit, + ) { + val url = if(season == null) { + "$susflixAPI/view/movie/$tmdbId" + } else { + "$susflixAPI/view/tv/$tmdbId/$season/$episode" + } + + val res = app.get(url,cookies = mapOf( + "session" to "eyJfZnJlc2giOmZhbHNlLCJwaG9uZV9udW1iZXIiOiJzdXNoZXg5OCJ9.ZO6CsA.XUs6Y5gna8ExAUX55-myMi1QpYU" + )).text.substringAfter("response = {").substringBefore("};").replace("\'", "\"") + + val sources = tryParseJson("{$res}") + sources?.qualities?.map { source -> + callback.invoke( + ExtractorLink( + "Susflix", + "Susflix", + source.path ?: return@map, + "$susflixAPI/", + getQualityFromName(source.quality) + ) + ) + } + + sources?.srtfiles?.map { sub -> + subtitleCallback.invoke( + SubtitleFile( + sub.caption ?: return@map, + sub.url ?: return@map, + ) + ) + } + + } + + suspend fun invokeJump1( + tmdbId: Int? = null, + tvdbId: Int? = null, + title: String? = null, + year: Int? = null, + season: Int? = null, + episode: Int? = null, + callback: (ExtractorLink) -> Unit, + ) { + val referer = "https://jump1.net/" + val res = if(season == null) { + val body = """{"filters":[{"type":"slug","args":{"slugs":["${title.createSlug()}-$year"]}}],"sort":"addedRecent","skip":0,"limit":100}""".toRequestBody(RequestBodyTypes.JSON.toMediaTypeOrNull()) + app.post("$jump1API/api/movies", requestBody = body, referer = referer) + } else { + app.get("$jump1API/api/shows/$tvdbId/seasons", referer = referer) + }.text + + val source = if(season == null) { + tryParseJson(res)?.movies?.find { it.id == tmdbId }?.videoId + } else { + val jumpSeason = tryParseJson>(res)?.find { it.seasonNumber == season }?.id + val seasonRes = app.get("$jump1API/api/shows/seasons/${jumpSeason ?: return}/episodes", referer = referer) + tryParseJson>(seasonRes.text)?.find { it.episodeNumber == episode }?.videoId + } + + callback.invoke( + ExtractorLink( + "Jump1", + "Jump1", + "$jump1API/hls/${source ?: return}/master.m3u8?ts=${APIHolder.unixTimeMS}", + referer, + Qualities.P1080.value, + true + ) + ) + } + } -class TravelR : GMPlayer() { - override val name = "TravelR" - override val mainUrl = "https://travel-russia.xyz" -} - -class Mwish : Filesim() { - override val name = "Mwish" - override var mainUrl = "https://mwish.pro" -} - -class Animefever : Filesim() { - override val name = "Animefever" - override var mainUrl = "https://animefever.fun" -} - -class Multimovies : Filesim() { - override val name = "Multimovies" - override var mainUrl = "https://multimovies.cloud" -} - -class MultimoviesSB : StreamSB() { - override var name = "Multimovies" - override var mainUrl = "https://multimovies.website" -} - -class Yipsu : Voe() { - override val name = "Yipsu" - override var mainUrl = "https://yip.su" -} - diff --git a/SoraStream/src/main/kotlin/com/hexated/SoraParser.kt b/SoraStream/src/main/kotlin/com/hexated/SoraParser.kt index db94285e..0897e838 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraParser.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraParser.kt @@ -65,7 +65,8 @@ data class HdMovieBoxIframe( data class ResponseHash( @JsonProperty("embed_url") val embed_url: String, - @JsonProperty("type") val type: String?, + @JsonProperty("key") val key: String? = null, + @JsonProperty("type") val type: String? = null, ) data class KisskhSources( @@ -87,11 +88,41 @@ data class KisskhDetail( @JsonProperty("episodes") val episodes: ArrayList? = arrayListOf(), ) +data class SusflixSrtfiles( + @JsonProperty("caption") val caption: String? = null, + @JsonProperty("url") val url: String? = null, +) + +data class SusflixQualities( + @JsonProperty("path") val path: String? = null, + @JsonProperty("quality") val quality: String? = null, +) + +data class SusflixSources( + @JsonProperty("Qualities") val qualities: ArrayList? = arrayListOf(), + @JsonProperty("Srtfiles") val srtfiles: ArrayList? = arrayListOf(), +) + data class KisskhResults( @JsonProperty("id") val id: Int?, @JsonProperty("title") val title: String?, ) +data class Jump1Episodes( + @JsonProperty("id") val id: Any? = null, + @JsonProperty("episodeNumber") val episodeNumber: Int? = null, + @JsonProperty("videoId") val videoId: String? = null, +) + +data class Jump1Season( + @JsonProperty("seasonNumber") val seasonNumber: Int? = null, + @JsonProperty("id") val id: String? = null, +) + +data class Jump1Movies( + @JsonProperty("movies") val movies: ArrayList? = arrayListOf(), +) + data class EpisodesFwatayako( @JsonProperty("id") val id: String? = null, @JsonProperty("file") val file: String? = null, @@ -206,25 +237,6 @@ data class WatchOnlineResponse( @JsonProperty("subtitles") val subtitles: Any? = null, ) -data class PutlockerEpisodes( - @JsonProperty("html") val html: String? = null, -) - -data class PutlockerEmbed( - @JsonProperty("src") val src: String? = null, -) - -data class PutlockerSources( - @JsonProperty("file") val file: String, - @JsonProperty("label") val label: String? = null, - @JsonProperty("type") val type: String? = null, -) - -data class PutlockerResponses( - @JsonProperty("sources") val sources: ArrayList? = arrayListOf(), - @JsonProperty("backupLink") val backupLink: String? = null, -) - data class CryMoviesProxyHeaders( @JsonProperty("request") val request: Map?, ) diff --git a/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt b/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt index 16c74135..ea77a419 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt @@ -34,7 +34,6 @@ import com.hexated.SoraExtractor.invokeMoviezAdd import com.hexated.SoraExtractor.invokeNavy import com.hexated.SoraExtractor.invokeNinetv import com.hexated.SoraExtractor.invokeNowTv -import com.hexated.SoraExtractor.invokePutlocker import com.hexated.SoraExtractor.invokeRStream import com.hexated.SoraExtractor.invokeRidomovies import com.hexated.SoraExtractor.invokeShinobiMovies @@ -42,11 +41,13 @@ import com.hexated.SoraExtractor.invokeSmashyStream import com.hexated.SoraExtractor.invokeDumpStream import com.hexated.SoraExtractor.invokeEmovies import com.hexated.SoraExtractor.invokeFourCartoon +import com.hexated.SoraExtractor.invokeJump1 import com.hexated.SoraExtractor.invokeMoment import com.hexated.SoraExtractor.invokeMultimovies import com.hexated.SoraExtractor.invokeNetmovies import com.hexated.SoraExtractor.invokePobmovies import com.hexated.SoraExtractor.invokePrimewire +import com.hexated.SoraExtractor.invokeSusflix import com.hexated.SoraExtractor.invokeTvMovies import com.hexated.SoraExtractor.invokeUhdmovies import com.hexated.SoraExtractor.invokeVidsrcto @@ -55,7 +56,6 @@ import com.hexated.SoraExtractor.invokeWatchsomuch import com.lagradost.cloudstream3.LoadResponse.Companion.addImdbId import com.lagradost.cloudstream3.LoadResponse.Companion.addTMDbId import com.lagradost.cloudstream3.extractors.VidSrcExtractor -import com.lagradost.cloudstream3.syncproviders.SyncIdName import com.lagradost.cloudstream3.utils.AppUtils.parseJson import com.lagradost.cloudstream3.utils.AppUtils.toJson import com.lagradost.cloudstream3.utils.ExtractorLink @@ -94,7 +94,7 @@ open class SoraStream : TmdbProvider() { const val hdMovieBoxAPI = "https://hdmoviebox.net" const val dreamfilmAPI = "https://dreamfilmsw.net" const val series9API = "https://series9.cx" - const val idlixAPI = "https://tv.idlixprime.com" + const val idlixAPI = "https://tv.idlixplus.net" const val noverseAPI = "https://www.nollyverse.com" const val filmxyAPI = "https://www.filmxy.vip" const val kimcartoonAPI = "https://kimcartoon.li" @@ -102,7 +102,7 @@ open class SoraStream : TmdbProvider() { const val crunchyrollAPI = "https://beta-api.crunchyroll.com" const val kissKhAPI = "https://kisskh.co" const val lingAPI = "https://ling-online.net" - const val uhdmoviesAPI = "https://uhdmovies.actor" + const val uhdmoviesAPI = "https://uhdmovies.wiki" const val fwatayakoAPI = "https://5100.svetacdn.in" const val gMoviesAPI = "https://gdrivemovies.xyz" const val fdMoviesAPI = "https://freedrivemovie.lol" @@ -118,7 +118,6 @@ open class SoraStream : TmdbProvider() { const val ask4MoviesAPI = "https://ask4movie.nl" const val watchOnlineAPI = "https://watchonline.ag" const val nineTvAPI = "https://moviesapi.club" - const val putlockerAPI = "https://ww7.putlocker.vip" const val fmoviesAPI = "https://fmovies.to" const val nowTvAPI = "https://myfilestorage.xyz" const val gokuAPI = "https://goku.sx" @@ -127,7 +126,7 @@ open class SoraStream : TmdbProvider() { const val emoviesAPI = "https://emovies.si" const val pobmoviesAPI = "https://pobmovies.cam" const val fourCartoonAPI = "https://4cartoon.net" - const val multimoviesAPI = "https://multimovies.xyz" + const val multimoviesAPI = "https://multi-movies.xyz" const val netmoviesAPI = "https://netmovies.to" const val momentAPI = "https://moment-explanation-i-244.site" const val doomoviesAPI = "https://doomovies.net" @@ -135,6 +134,8 @@ open class SoraStream : TmdbProvider() { const val vidsrctoAPI = "https://vidsrc.to" const val dramadayAPI = "https://dramaday.me" const val animetoshoAPI = "https://animetosho.org" + const val susflixAPI = "https://susflix.tv" + const val jump1API = "https://ca.jump1.net" // INDEX SITE const val dahmerMoviesAPI = "https://edytjedhgmdhm.abfhaqrhbnf.workers.dev" @@ -279,6 +280,7 @@ open class SoraStream : TmdbProvider() { LinkData( data.id, res.external_ids?.imdb_id, + res.external_ids?.tvdb_id, data.type, eps.seasonNumber, eps.episodeNumber, @@ -332,6 +334,7 @@ open class SoraStream : TmdbProvider() { LinkData( data.id, res.external_ids?.imdb_id, + res.external_ids?.tvdb_id, data.type, title = title, year = year, @@ -536,7 +539,7 @@ open class SoraStream : TmdbProvider() { ) }, { - invokeM4uhd( + if(!res.isAnime) invokeM4uhd( res.title, res.year, res.season, @@ -545,9 +548,6 @@ open class SoraStream : TmdbProvider() { callback ) }, - { - invokePutlocker(res.title, res.year, res.season, res.episode, callback) - }, { invokeTvMovies(res.title, res.season, res.episode, callback) }, @@ -746,6 +746,20 @@ open class SoraStream : TmdbProvider() { { if(!res.isAnime) invoke2embed(res.imdbId,res.season,res.episode,callback) }, +// { +// invokeSusflix(res.id,res.season,res.episode,subtitleCallback,callback) +// }, + { + if(!res.isAnime) invokeJump1( + res.id, + res.tvdbId, + res.title, + res.year, + res.season, + res.episode, + callback + ) + }, ) return true @@ -754,6 +768,7 @@ open class SoraStream : TmdbProvider() { data class LinkData( val id: Int? = null, val imdbId: String? = null, + val tvdbId: Int? = null, val type: String? = null, val season: Int? = null, val episode: Int? = null, @@ -858,7 +873,7 @@ open class SoraStream : TmdbProvider() { data class ExternalIds( @JsonProperty("imdb_id") val imdb_id: String? = null, - @JsonProperty("tvdb_id") val tvdb_id: String? = null, + @JsonProperty("tvdb_id") val tvdb_id: Int? = null, ) data class Credits( diff --git a/SoraStream/src/main/kotlin/com/hexated/SoraStreamLite.kt b/SoraStream/src/main/kotlin/com/hexated/SoraStreamLite.kt index c5bf1a6d..2703133e 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraStreamLite.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraStreamLite.kt @@ -21,7 +21,6 @@ import com.hexated.SoraExtractor.invokeMovieHab import com.hexated.SoraExtractor.invokeNavy import com.hexated.SoraExtractor.invokeNinetv import com.hexated.SoraExtractor.invokeNowTv -import com.hexated.SoraExtractor.invokePutlocker import com.hexated.SoraExtractor.invokeRStream import com.hexated.SoraExtractor.invokeRidomovies import com.hexated.SoraExtractor.invokeSeries9 @@ -29,10 +28,12 @@ import com.hexated.SoraExtractor.invokeSmashyStream import com.hexated.SoraExtractor.invokeDumpStream import com.hexated.SoraExtractor.invokeEmovies import com.hexated.SoraExtractor.invokeFourCartoon +import com.hexated.SoraExtractor.invokeJump1 import com.hexated.SoraExtractor.invokeMoment import com.hexated.SoraExtractor.invokeMultimovies import com.hexated.SoraExtractor.invokeNetmovies import com.hexated.SoraExtractor.invokePrimewire +import com.hexated.SoraExtractor.invokeSusflix import com.hexated.SoraExtractor.invokeVidSrc import com.hexated.SoraExtractor.invokeVidsrcto import com.hexated.SoraExtractor.invokeWatchOnline @@ -56,14 +57,17 @@ class SoraStreamLite : SoraStream() { argamap( { - invokePutlocker( - res.title, - res.year, - res.season, - res.episode, - callback - ) + if(!res.isAnime) invokeJump1(res.id,res.tvdbId,res.title,res.year,res.season,res.episode,callback) }, +// { +// invokeSusflix( +// res.id, +// res.season, +// res.episode, +// subtitleCallback, +// callback +// ) +// }, { invokeWatchsomuch( res.imdbId, @@ -253,7 +257,7 @@ class SoraStreamLite : SoraStream() { invokeFwatayako(res.imdbId, res.season, res.episode, callback) }, { - invokeM4uhd( + if(!res.isAnime) invokeM4uhd( res.title, res.year, res.season, diff --git a/SoraStream/src/main/kotlin/com/hexated/SoraStreamPlugin.kt b/SoraStream/src/main/kotlin/com/hexated/SoraStreamPlugin.kt index 7bb7f262..3e0cda41 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraStreamPlugin.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraStreamPlugin.kt @@ -17,5 +17,6 @@ class SoraStreamPlugin: Plugin() { registerExtractorAPI(Yipsu()) registerExtractorAPI(Mwish()) registerExtractorAPI(TravelR()) + registerExtractorAPI(Playm4u()) } } \ 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 f256bf87..09f6f822 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraUtils.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraUtils.kt @@ -10,7 +10,6 @@ import com.hexated.SoraStream.Companion.filmxyAPI import com.hexated.SoraStream.Companion.fmoviesAPI import com.hexated.SoraStream.Companion.gdbot import com.hexated.SoraStream.Companion.malsyncAPI -import com.hexated.SoraStream.Companion.putlockerAPI import com.hexated.SoraStream.Companion.smashyStreamAPI import com.hexated.SoraStream.Companion.tvMoviesAPI import com.hexated.SoraStream.Companion.watchOnlineAPI @@ -45,7 +44,6 @@ import kotlin.math.min val bflixChipperKey = base64DecodeAPI("Yjc=ejM=TzA=YTk=WHE=WnU=bXU=RFo=") const val bflixKey = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" -const val otakuzBaseUrl = "https://otakuz.live/" val encodedIndex = arrayOf( "GamMovies", "JSMovies", @@ -1055,52 +1053,6 @@ suspend fun getCrunchyrollIdFromMalSync(aniId: String?): String? { ?: regex.find("$crunchyroll")?.groupValues?.getOrNull(1) } -suspend fun extractPutlockerSources(url: String?): NiceResponse? { - val embedHost = url?.substringBefore("/embed-player") - val player = app.get( - url ?: return null, - referer = "${putlockerAPI}/" - ).document.select("div#player") - - val text = "\"${player.attr("data-id")}\"" - val password = player.attr("data-hash") - val cipher = CryptoAES.plEncrypt(password, text) - - return app.get( - "$embedHost/ajax/getSources/", params = mapOf( - "id" to cipher.cipherText, - "h" to cipher.password, - "a" to cipher.iv, - "t" to cipher.salt, - ), referer = url - ) -} - -suspend fun PutlockerResponses?.callback( - referer: String, - server: String, - callback: (ExtractorLink) -> Unit -) { - val ref = getBaseUrl(referer) - this?.sources?.map { source -> - val request = app.get(source.file, referer = ref) - callback.invoke( - ExtractorLink( - "Putlocker [$server]", - "Putlocker [$server]", - if (!request.isSuccessful) return@map null else source.file, - ref, - if (source.file.contains("m3u8")) getPutlockerQuality(request.text) else source.label?.replace( - Regex("[Pp]"), - "" - )?.trim()?.toIntOrNull() - ?: Qualities.P720.value, - source.file.contains("m3u8") - ) - ) - } -} - suspend fun convertTmdbToAnimeId( title: String?, date: String?, @@ -1191,10 +1143,10 @@ suspend fun loadCustomExtractor( link.url, link.referer, when { - link.isM3u8 -> link.quality + link.type == ExtractorLinkType.M3U8 -> link.quality else -> quality ?: link.quality }, - link.isM3u8, + link.type, link.headers, link.extractorData ) @@ -1655,161 +1607,6 @@ private enum class Symbol(val decimalValue: Int) { } } -// code found on https://stackoverflow.com/a/63701411 - -/** - * Conforming with CryptoJS AES method - */ -// see https://gist.github.com/thackerronak/554c985c3001b16810af5fc0eb5c358f -@Suppress("unused", "FunctionName", "SameParameterValue") -object CryptoAES { - - private const val KEY_SIZE = 256 - private const val IV_SIZE = 128 - private const val HASH_CIPHER = "AES/CBC/PKCS5Padding" - private const val AES = "AES" - private const val KDF_DIGEST = "MD5" - - // Seriously crypto-js, what's wrong with you? - private const val APPEND = "Salted__" - - /** - * Encrypt - * @param password passphrase - * @param plainText plain string - */ - fun encrypt(password: String, plainText: String): String { - val saltBytes = generateSalt(8) - val key = ByteArray(KEY_SIZE / 8) - val iv = ByteArray(IV_SIZE / 8) - EvpKDF(password.toByteArray(), KEY_SIZE, IV_SIZE, saltBytes, key, iv) - val keyS = SecretKeySpec(key, AES) - val cipher = Cipher.getInstance(HASH_CIPHER) - val ivSpec = IvParameterSpec(iv) - cipher.init(Cipher.ENCRYPT_MODE, keyS, ivSpec) - val cipherText = cipher.doFinal(plainText.toByteArray()) - // Thanks kientux for this: https://gist.github.com/kientux/bb48259c6f2133e628ad - // Create CryptoJS-like encrypted! - val sBytes = APPEND.toByteArray() - val b = ByteArray(sBytes.size + saltBytes.size + cipherText.size) - System.arraycopy(sBytes, 0, b, 0, sBytes.size) - System.arraycopy(saltBytes, 0, b, sBytes.size, saltBytes.size) - System.arraycopy(cipherText, 0, b, sBytes.size + saltBytes.size, cipherText.size) - val bEncode = Base64.encode(b, Base64.NO_WRAP) - return String(bEncode) - } - - fun plEncrypt(password: String, plainText: String): EncryptResult { - val saltBytes = generateSalt(8) - val key = ByteArray(KEY_SIZE / 8) - val iv = ByteArray(IV_SIZE / 8) - EvpKDF(password.toByteArray(), KEY_SIZE, IV_SIZE, saltBytes, key, iv) - val keyS = SecretKeySpec(key, AES) - val cipher = Cipher.getInstance(HASH_CIPHER) - val ivSpec = IvParameterSpec(iv) - cipher.init(Cipher.ENCRYPT_MODE, keyS, ivSpec) - val cipherText = cipher.doFinal(plainText.toByteArray()) - val bEncode = Base64.encode(cipherText, Base64.NO_WRAP) - return EncryptResult( - String(bEncode).toHex(), - password.toHex(), - saltBytes.toHex(), - iv.toHex() - ) - } - - /** - * Decrypt - * Thanks Artjom B. for this: http://stackoverflow.com/a/29152379/4405051 - * @param password passphrase - * @param cipherText encrypted string - */ - fun decrypt(password: String, cipherText: String): String { - val ctBytes = Base64.decode(cipherText.toByteArray(), Base64.NO_WRAP) - val saltBytes = Arrays.copyOfRange(ctBytes, 8, 16) - val cipherTextBytes = Arrays.copyOfRange(ctBytes, 16, ctBytes.size) - val key = ByteArray(KEY_SIZE / 8) - val iv = ByteArray(IV_SIZE / 8) - EvpKDF(password.toByteArray(), KEY_SIZE, IV_SIZE, saltBytes, key, iv) - val cipher = Cipher.getInstance(HASH_CIPHER) - val keyS = SecretKeySpec(key, AES) - cipher.init(Cipher.DECRYPT_MODE, keyS, IvParameterSpec(iv)) - val plainText = cipher.doFinal(cipherTextBytes) - return String(plainText) - } - - private fun EvpKDF( - password: ByteArray, - keySize: Int, - ivSize: Int, - salt: ByteArray, - resultKey: ByteArray, - resultIv: ByteArray - ): ByteArray { - return EvpKDF(password, keySize, ivSize, salt, 1, KDF_DIGEST, resultKey, resultIv) - } - - @Suppress("NAME_SHADOWING") - private fun EvpKDF( - password: ByteArray, - keySize: Int, - ivSize: Int, - salt: ByteArray, - iterations: Int, - hashAlgorithm: String, - resultKey: ByteArray, - resultIv: ByteArray - ): ByteArray { - val keySize = keySize / 32 - val ivSize = ivSize / 32 - val targetKeySize = keySize + ivSize - val derivedBytes = ByteArray(targetKeySize * 4) - var numberOfDerivedWords = 0 - var block: ByteArray? = null - val hash = MessageDigest.getInstance(hashAlgorithm) - while (numberOfDerivedWords < targetKeySize) { - if (block != null) { - hash.update(block) - } - hash.update(password) - block = hash.digest(salt) - hash.reset() - // Iterations - for (i in 1 until iterations) { - block = hash.digest(block!!) - hash.reset() - } - System.arraycopy( - block!!, 0, derivedBytes, numberOfDerivedWords * 4, - min(block.size, (targetKeySize - numberOfDerivedWords) * 4) - ) - numberOfDerivedWords += block.size / 4 - } - System.arraycopy(derivedBytes, 0, resultKey, 0, keySize * 4) - System.arraycopy(derivedBytes, keySize * 4, resultIv, 0, ivSize * 4) - return derivedBytes // key + iv - } - - private fun generateSalt(length: Int): ByteArray { - return ByteArray(length).apply { - SecureRandom().nextBytes(this) - } - } - - private fun ByteArray.toHex(): String = - joinToString(separator = "") { eachByte -> "%02x".format(eachByte) } - - private fun String.toHex(): String = toByteArray().toHex() - - data class EncryptResult( - val cipherText: String, - val password: String, - val salt: String, - val iv: String - ) - -} - object DumpUtils { private val deviceId = getDeviceId() @@ -1926,4 +1723,127 @@ object RSAEncryptionHelper { exception.printStackTrace() null } +} + +// code found on https://stackoverflow.com/a/63701411 + +/** + * Conforming with CryptoJS AES method + */ +// see https://gist.github.com/thackerronak/554c985c3001b16810af5fc0eb5c358f +@Suppress("unused", "FunctionName", "SameParameterValue") +object CryptoJS { + + private const val KEY_SIZE = 256 + private const val IV_SIZE = 128 + private const val HASH_CIPHER = "AES/CBC/PKCS7Padding" + private const val AES = "AES" + private const val KDF_DIGEST = "MD5" + + // Seriously crypto-js, what's wrong with you? + private const val APPEND = "Salted__" + + /** + * Encrypt + * @param password passphrase + * @param plainText plain string + */ + fun encrypt(password: String, plainText: String): String { + val saltBytes = generateSalt(8) + val key = ByteArray(KEY_SIZE / 8) + val iv = ByteArray(IV_SIZE / 8) + EvpKDF(password.toByteArray(), KEY_SIZE, IV_SIZE, saltBytes, key, iv) + val keyS = SecretKeySpec(key, AES) + val cipher = Cipher.getInstance(HASH_CIPHER) + val ivSpec = IvParameterSpec(iv) + cipher.init(Cipher.ENCRYPT_MODE, keyS, ivSpec) + val cipherText = cipher.doFinal(plainText.toByteArray()) + // Thanks kientux for this: https://gist.github.com/kientux/bb48259c6f2133e628ad + // Create CryptoJS-like encrypted! + val sBytes = APPEND.toByteArray() + val b = ByteArray(sBytes.size + saltBytes.size + cipherText.size) + System.arraycopy(sBytes, 0, b, 0, sBytes.size) + System.arraycopy(saltBytes, 0, b, sBytes.size, saltBytes.size) + System.arraycopy(cipherText, 0, b, sBytes.size + saltBytes.size, cipherText.size) + val bEncode = Base64.encode(b, Base64.NO_WRAP) + return String(bEncode) + } + + /** + * Decrypt + * Thanks Artjom B. for this: http://stackoverflow.com/a/29152379/4405051 + * @param password passphrase + * @param cipherText encrypted string + */ + fun decrypt(password: String, cipherText: String): String { + val ctBytes = Base64.decode(cipherText.toByteArray(), Base64.NO_WRAP) + val saltBytes = Arrays.copyOfRange(ctBytes, 8, 16) + val cipherTextBytes = Arrays.copyOfRange(ctBytes, 16, ctBytes.size) + val key = ByteArray(KEY_SIZE / 8) + val iv = ByteArray(IV_SIZE / 8) + EvpKDF(password.toByteArray(), KEY_SIZE, IV_SIZE, saltBytes, key, iv) + val cipher = Cipher.getInstance(HASH_CIPHER) + val keyS = SecretKeySpec(key, AES) + cipher.init(Cipher.DECRYPT_MODE, keyS, IvParameterSpec(iv)) + val plainText = cipher.doFinal(cipherTextBytes) + return String(plainText) + } + + private fun EvpKDF( + password: ByteArray, + keySize: Int, + ivSize: Int, + salt: ByteArray, + resultKey: ByteArray, + resultIv: ByteArray + ): ByteArray { + return EvpKDF(password, keySize, ivSize, salt, 1, KDF_DIGEST, resultKey, resultIv) + } + + @Suppress("NAME_SHADOWING") + private fun EvpKDF( + password: ByteArray, + keySize: Int, + ivSize: Int, + salt: ByteArray, + iterations: Int, + hashAlgorithm: String, + resultKey: ByteArray, + resultIv: ByteArray + ): ByteArray { + val keySize = keySize / 32 + val ivSize = ivSize / 32 + val targetKeySize = keySize + ivSize + val derivedBytes = ByteArray(targetKeySize * 4) + var numberOfDerivedWords = 0 + var block: ByteArray? = null + val hash = MessageDigest.getInstance(hashAlgorithm) + while (numberOfDerivedWords < targetKeySize) { + if (block != null) { + hash.update(block) + } + hash.update(password) + block = hash.digest(salt) + hash.reset() + // Iterations + for (i in 1 until iterations) { + block = hash.digest(block!!) + hash.reset() + } + System.arraycopy( + block!!, 0, derivedBytes, numberOfDerivedWords * 4, + min(block.size, (targetKeySize - numberOfDerivedWords) * 4) + ) + numberOfDerivedWords += block.size / 4 + } + System.arraycopy(derivedBytes, 0, resultKey, 0, keySize * 4) + System.arraycopy(derivedBytes, keySize * 4, resultIv, 0, ivSize * 4) + return derivedBytes // key + iv + } + + private fun generateSalt(length: Int): ByteArray { + return ByteArray(length).apply { + SecureRandom().nextBytes(this) + } + } } \ No newline at end of file diff --git a/YomoviesProvider/src/main/kotlin/com/hexated/YomoviesProvider.kt b/YomoviesProvider/src/main/kotlin/com/hexated/YomoviesProvider.kt index 9f9fce7c..f75772cc 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.ltd" + override var mainUrl = "https://yomovies.fan" private var directUrl = "" override var name = "Yomovies" override val hasMainPage = true diff --git a/settings.gradle.kts b/settings.gradle.kts index f2497ec2..5df21340 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -2,7 +2,7 @@ rootProject.name = "CloudstreamPlugins" // This file sets what projects are included. All new projects should get automatically included unless specified in "disabled" variable. -val disabled = listOf("Animixplay") +val disabled = listOf("Animixplay","Kickassanime") File(rootDir, ".").eachDir { dir -> if (!disabled.contains(dir.name) && File(dir, "build.gradle.kts").exists()) {