diff --git a/Anichi/build.gradle.kts b/Anichi/build.gradle.kts index 16cb91cb..24eea240 100644 --- a/Anichi/build.gradle.kts +++ b/Anichi/build.gradle.kts @@ -1,7 +1,7 @@ import org.jetbrains.kotlin.konan.properties.Properties // use an integer for version numbers -version = 2 +version = 4 android { defaultConfig { diff --git a/Anichi/src/main/kotlin/com/hexated/Anichi.kt b/Anichi/src/main/kotlin/com/hexated/Anichi.kt index 372b65f5..02d0fe98 100644 --- a/Anichi/src/main/kotlin/com/hexated/Anichi.kt +++ b/Anichi/src/main/kotlin/com/hexated/Anichi.kt @@ -12,6 +12,7 @@ import com.lagradost.cloudstream3.mvvm.safeApiCall import com.lagradost.cloudstream3.utils.* import com.lagradost.cloudstream3.utils.AppUtils.parseJson import com.lagradost.cloudstream3.utils.AppUtils.toJson +import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson import com.lagradost.nicehttp.RequestBodyTypes import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.RequestBody.Companion.toRequestBody @@ -42,73 +43,54 @@ class Anichi : MainAPI() { override val supportedTypes = setOf(TvType.Anime, TvType.AnimeMovie) private val popularTitle = "Popular" - private val recentTitle = "Latest Updated" - override val mainPage = listOf( - MainPageData( - recentTitle, - """$apiUrl?variables={"search":{"sortBy":"Latest_Update","allowAdult":${settingsForProvider.enableAdult},"allowUnknown":false},"limit":26,"page":%d,"translationType":"sub","countryOrigin":"ALL"}&extensions={"persistedQuery":{"version":1,"sha256Hash":"$mainHash"}}""" - ), - MainPageData( - popularTitle, - """$apiUrl?variables={"type":"anime","size":30,"dateRange":1,"page":%d,"allowAdult":${settingsForProvider.enableAdult},"allowUnknown":false}&extensions={"persistedQuery":{"version":1,"sha256Hash":"$popularHash"}}""" - ) + private val animeRecentTitle = "Latest Anime" + private val donghuaRecentTitle = "Latest Donghua" + private val movieTitle = "Movie" + + override val mainPage = mainPageOf( + """$apiUrl?variables={"search":{"sortBy":"Latest_Update","allowAdult":${settingsForProvider.enableAdult},"allowUnknown":false},"limit":26,"page":%d,"translationType":"sub","countryOrigin":"JP"}&extensions={"persistedQuery":{"version":1,"sha256Hash":"$mainHash"}}""" to animeRecentTitle, + """$apiUrl?variables={"search":{"sortBy":"Latest_Update","allowAdult":${settingsForProvider.enableAdult},"allowUnknown":false},"limit":26,"page":%d,"translationType":"sub","countryOrigin":"CN"}&extensions={"persistedQuery":{"version":1,"sha256Hash":"$mainHash"}}""" to donghuaRecentTitle, + """$apiUrl?variables={"type":"anime","size":30,"dateRange":1,"page":%d,"allowAdult":${settingsForProvider.enableAdult},"allowUnknown":false}&extensions={"persistedQuery":{"version":1,"sha256Hash":"$popularHash"}}""" to popularTitle, + """$apiUrl?variables={"search":{"slug":"movie-anime","format":"anime","tagType":"upcoming","name":"Trending Movies"}}&extensions={"persistedQuery":{"version":1,"sha256Hash":"$slugHash"}}""" to movieTitle ) override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse { val url = request.data.format(page) - val test = app.get(url, headers = headers).text - - val home = when (request.name) { - recentTitle -> { - val json = parseJson(test) - val results = json.data.shows.edges.filter { - // filtering in case there is an anime with 0 episodes available on the site. - !(it.availableEpisodes?.raw == 0 && it.availableEpisodes.sub == 0 && it.availableEpisodes.dub == 0) - } - - results.map { - newAnimeSearchResponse(it.name ?: "", "${it.Id}", fix = false) { - this.posterUrl = it.thumbnail - this.year = it.airedStart?.year - this.otherName = it.englishName - addDub(it.availableEpisodes?.dub) - addSub(it.availableEpisodes?.sub) - } - } - } - popularTitle -> { - val json = parseJson(test) - val results = json.data?.queryPopular?.recommendations?.filter { - // filtering in case there is an anime with 0 episodes available on the site. - !(it.anyCard?.availableEpisodes?.raw == 0 && it.anyCard.availableEpisodes.sub == 0 && it.anyCard.availableEpisodes.dub == 0) - } - results?.mapNotNull { - newAnimeSearchResponse( - it.anyCard?.name ?: return@mapNotNull null, - "${it.anyCard.Id ?: it.pageStatus?.Id}", - fix = false - ) { - this.posterUrl = it.anyCard.thumbnail - this.otherName = it.anyCard.englishName - addDub(it.anyCard.availableEpisodes?.dub) - addSub(it.anyCard.availableEpisodes?.sub) - } - } ?: emptyList() - } - else -> emptyList() - } - - - - return HomePageResponse( - listOf( - HomePageList(request.name, home) - ), hasNext = home.isNotEmpty() + val res = app.get(url, headers = headers).parsedSafe()?.data + val query = res?.shows ?: res?.queryPopular ?: res?.queryListForTag + val card = if(request.name == popularTitle) query?.recommendations?.map { it.anyCard } else query?.edges + val home = card?.filter { + // filtering in case there is an anime with 0 episodes available on the site. + !(it?.availableEpisodes?.raw == 0 && it.availableEpisodes.sub == 0 && it.availableEpisodes.dub == 0) + }?.mapNotNull { media -> + media?.toSearchResponse() + } ?: emptyList() + return newHomePageResponse( + list = HomePageList( + name = request.name, + list = home, + ), + hasNext = request.name != movieTitle ) } - override suspend fun search(query: String): List { + private fun Edges.toSearchResponse(): AnimeSearchResponse? { + + return newAnimeSearchResponse( + name ?: englishName ?: nativeName ?: "", + Id ?: return null, + fix = false + ) { + this.posterUrl = thumbnail + this.year = airedStart?.year + this.otherName = englishName + addDub(availableEpisodes?.dub) + addSub(availableEpisodes?.sub) + } + } + + override suspend fun search(query: String): List? { val link = """$apiUrl?variables={"search":{"allowAdult":false,"allowUnknown":false,"query":"$query"},"limit":26,"page":1,"translationType":"sub","countryOrigin":"ALL"}&extensions={"persistedQuery":{"version":1,"sha256Hash":"$mainHash"}}""" @@ -125,12 +107,12 @@ class Anichi : MainAPI() { val response = parseJson(res) - val results = response.data.shows.edges.filter { + val results = response.data?.shows?.edges?.filter { // filtering in case there is an anime with 0 episodes available on the site. !(it.availableEpisodes?.raw == 0 && it.availableEpisodes.sub == 0 && it.availableEpisodes.dub == 0) } - return results.map { + return results?.map { newAnimeSearchResponse(it.name ?: "", "${it.Id}", fix = false) { this.posterUrl = it.thumbnail this.year = it.airedStart?.year @@ -161,19 +143,13 @@ class Anichi : MainAPI() { val poster = showData.thumbnail val type = getType(showData.type ?: "") - val episodes = showData.availableEpisodes.let { + val episodes = showData.availableEpisodesDetail.let { if (it == null) return@let Pair(null, null) if (showData.Id == null) return@let Pair(null, null) - - Pair(if (it.sub != 0) ((1..it.sub).map { epNum -> - Episode( - AnichiLoadData(showData.Id, "sub", epNum).toJson(), episode = epNum - ) - }) else null, if (it.dub != 0) ((1..it.dub).map { epNum -> - Episode( - AnichiLoadData(showData.Id, "dub", epNum).toJson(), episode = epNum - ) - }) else null) + Pair( + it.getEpisode("sub", showData.Id), + it.getEpisode("dub", showData.Id), + ) } val characters = showData.characters?.map { @@ -191,7 +167,7 @@ class Anichi : MainAPI() { val names = showData.altNames?.plus(title)?.filterNotNull() ?: emptyList() val trackers = getTracker(names, TrackerType.getTypes(type), showData.airedStart?.year) - return newAnimeLoadResponse(title ?: "", url, type) { + return newAnimeLoadResponse(title ?: "", url, TvType.Anime) { engName = showData.altNames?.firstOrNull() posterUrl = trackers?.image ?: poster backgroundPosterUrl = trackers?.cover ?: showData.banner @@ -229,7 +205,7 @@ class Anichi : MainAPI() { apiResponse.data?.episode?.sourceUrls?.apmap { source -> safeApiCall { - val link = source.sourceUrl?.replace(" ", "%20") ?: return@safeApiCall + val link = fixSourceUrls(source.sourceUrl ?: return@safeApiCall, source.sourceName) ?: return@safeApiCall if (URI(link).isAbsolute || link.startsWith("//")) { val fixedLink = if (link.startsWith("//")) "https:$link" else link val host = link.getHost() @@ -258,7 +234,7 @@ class Anichi : MainAPI() { } } } else { - val fixedLink = apiEndPoint + URI(link).path + ".json?" + URI(link).query + val fixedLink = link.fixUrlPath() val links = app.get(fixedLink).parsedSafe()?.links ?: emptyList() links.forEach { server -> @@ -293,7 +269,8 @@ class Anichi : MainAPI() { ).path), server.resolutionStr.removeSuffix("p").toIntOrNull() ?: Qualities.P1080.value, - false + false, + isDash = server.resolutionStr == "Dash 1" ) ) } @@ -330,6 +307,19 @@ class Anichi : MainAPI() { return false } + private fun AvailableEpisodesDetail.getEpisode( + lang: String, + id: String + ): List { + val meta = if (lang == "sub") this.sub else this.dub + return meta.map { eps -> + Episode( + AnichiLoadData(id, lang, eps).toJson(), + "Ep $eps" + ) + }.reversed() + } + private suspend fun getM3u8Qualities( m3u8Link: String, referer: String, @@ -380,6 +370,18 @@ class Anichi : MainAPI() { return fixTitle(URI(this).host.substringBeforeLast(".").substringAfterLast(".")) } + private fun String.fixUrlPath() : String { + return if(this.contains(".json?")) apiEndPoint + this else apiEndPoint + URI(this).path + ".json?" + URI(this).query + } + + private fun fixSourceUrls(url: String, source: String?) : String? { + return if(source == "Ak" || url.contains("/player/vitemb")) { + tryParseJson(base64Decode(url.substringAfter("=")))?.idUrl + } else { + url.replace(" ", "%20") + } + } + companion object { private const val apiUrl = BuildConfig.ANICHI_API private const val serverUrl = BuildConfig.ANICHI_SERVER @@ -387,6 +389,7 @@ class Anichi : MainAPI() { private const val mainHash = "e42a4466d984b2c0a2cecae5dd13aa68867f634b16ee0f17b380047d14482406" private const val popularHash = "31a117653812a2547fd981632e8c99fa8bf8a75c4ef1a77a1567ef1741a7ab9c" + private const val slugHash = "bf603205eb2533ca21d0324a11f623854d62ed838a27e1b3fcfb712ab98b03f4" private const val detailHash = "bb263f91e5bdd048c1c978f324613aeccdfe2cbc694a419466a31edb58c0cc0b" private const val serverHash = "5e7e17cdd0166af5a2d8f43133d9ce3ce9253d1fdb5160a0cfd515564f98d061" @@ -400,7 +403,11 @@ class Anichi : MainAPI() { data class AnichiLoadData( val hash: String, val dubStatus: String, - val episode: Int + val episode: String + ) + + data class AkIframe( + @JsonProperty("idUrl") val idUrl: String? = null, ) data class Stream( @@ -414,12 +421,19 @@ class Anichi : MainAPI() { @JsonProperty("streams") val streams: ArrayList? = arrayListOf(), ) + data class Subtitles( + @JsonProperty("lang") val lang: String?, + @JsonProperty("label") val label: String?, + @JsonProperty("src") val src: String?, + ) + data class Links( @JsonProperty("link") val link: String, @JsonProperty("hls") val hls: Boolean?, @JsonProperty("resolutionStr") val resolutionStr: String, @JsonProperty("src") val src: String?, @JsonProperty("portData") val portData: PortData? = null, + @JsonProperty("subtitles") val subtitles: ArrayList? = arrayListOf(), ) data class AnichiVideoApiResponse( @@ -427,13 +441,18 @@ class Anichi : MainAPI() { ) data class Data( - @JsonProperty("shows") val shows: Shows + @JsonProperty("shows") val shows: Shows? = null, + @JsonProperty("queryListForTag") val queryListForTag: Shows? = null, + @JsonProperty("queryPopular") val queryPopular: Shows? = null, ) data class Shows( - @JsonProperty("pageInfo") val pageInfo: PageInfo, - @JsonProperty("edges") val edges: List, - @JsonProperty("__typename") val _typename: String? + @JsonProperty("edges") val edges: List? = arrayListOf(), + @JsonProperty("recommendations") val recommendations: List? = arrayListOf(), + ) + + data class EdgesCard( + @JsonProperty("anyCard") val anyCard: Edges? = null, ) data class CharacterImage( @@ -493,13 +512,8 @@ class Anichi : MainAPI() { @JsonProperty("year") val year: Int ) - data class PageInfo( - @JsonProperty("total") val total: Int?, - @JsonProperty("__typename") val _typename: String? - ) - data class AnichiQuery( - @JsonProperty("data") val data: Data + @JsonProperty("data") val data: Data? = null ) data class Detail( @@ -537,10 +551,6 @@ class Anichi : MainAPI() { @JsonProperty("sourceUrls") val sourceUrls: ArrayList = arrayListOf(), ) - data class PopularQuery( - @JsonProperty("data") val data: DataPopular? = DataPopular() - ) - data class Sub( @JsonProperty("hour") val hour: Int? = null, @JsonProperty("minute") val minute: Int? = null, diff --git a/Gomunimeis/build.gradle.kts b/Gomunimeis/build.gradle.kts index 5c6d720f..76ef54dd 100644 --- a/Gomunimeis/build.gradle.kts +++ b/Gomunimeis/build.gradle.kts @@ -1,5 +1,5 @@ // use an integer for version numbers -version = 5 +version = 6 cloudstream { diff --git a/Gomunimeis/src/main/kotlin/com/hexated/Gomunimeis.kt b/Gomunimeis/src/main/kotlin/com/hexated/Gomunimeis.kt index 43e8616e..afcfa844 100644 --- a/Gomunimeis/src/main/kotlin/com/hexated/Gomunimeis.kt +++ b/Gomunimeis/src/main/kotlin/com/hexated/Gomunimeis.kt @@ -129,7 +129,7 @@ class Gomunimeis : MainAPI() { .let { id -> app.get("https://gomunimes.com/stream?id=$id") .parsedSafe()?.server?.streamsb?.link?.let { link -> - loadExtractor(link, "https://vidgomunime.xyz/", subtitleCallback, callback) + loadExtractor(link.replace("vidgomunimesb.xyz", "watchsb.com"), mainUrl, subtitleCallback, callback) } } diff --git a/Minioppai/build.gradle.kts b/Minioppai/build.gradle.kts index b6d79f30..3d8a6e96 100644 --- a/Minioppai/build.gradle.kts +++ b/Minioppai/build.gradle.kts @@ -1,5 +1,5 @@ // use an integer for version numbers -version = 2 +version = 3 cloudstream { diff --git a/Minioppai/src/main/kotlin/com/hexated/Minioppai.kt b/Minioppai/src/main/kotlin/com/hexated/Minioppai.kt index 129a20b9..ece21181 100644 --- a/Minioppai/src/main/kotlin/com/hexated/Minioppai.kt +++ b/Minioppai/src/main/kotlin/com/hexated/Minioppai.kt @@ -165,14 +165,20 @@ class Minioppai : MainAPI() { val sources = script.substringAfter("sources:[").substringBefore("]").replace("'", "\"") val subtitles = script.substringAfter("\"tracks\":[").substringBefore("]") - tryParseJson>("[$sources]")?.map { + tryParseJson>("[$sources]")?.map { source -> + val pStream = fixLink(source.file ?: return@map, paistream).takeIf { + app.get( + it, + referer = "$paistream/" + ).isSuccessful + } callback.invoke( ExtractorLink( server, server, - fixLink(it.file ?: return@map, if(server == "Stream 1") libPaistream else paistream), + pStream ?: fixLink(source.file ?: return@map, libPaistream), "$paistream/", - getQualityFromName(it.label) + getQualityFromName(source.label) ) ) } diff --git a/Movierulzhd/build.gradle.kts b/Movierulzhd/build.gradle.kts index 1d596ef2..e25e9c0e 100644 --- a/Movierulzhd/build.gradle.kts +++ b/Movierulzhd/build.gradle.kts @@ -1,5 +1,5 @@ // use an integer for version numbers -version = 37 +version = 36 cloudstream { diff --git a/Movierulzhd/src/main/kotlin/com/hexated/Movierulzhd.kt b/Movierulzhd/src/main/kotlin/com/hexated/Movierulzhd.kt index 263375b7..26bc47d1 100644 --- a/Movierulzhd/src/main/kotlin/com/hexated/Movierulzhd.kt +++ b/Movierulzhd/src/main/kotlin/com/hexated/Movierulzhd.kt @@ -12,7 +12,7 @@ import org.jsoup.nodes.Element import java.net.URI class Movierulzhd : MainAPI() { - override var mainUrl = "https://movierulzhd.help" + override var mainUrl = "https://movierulzhd.trade" private var directUrl = mainUrl override var name = "Movierulzhd" override val hasMainPage = true diff --git a/Ngefilm/build.gradle.kts b/Ngefilm/build.gradle.kts index d8eb0226..3187c097 100644 --- a/Ngefilm/build.gradle.kts +++ b/Ngefilm/build.gradle.kts @@ -1,5 +1,5 @@ // use an integer for version numbers -version = 2 +version = 3 cloudstream { diff --git a/Ngefilm/src/main/kotlin/com/hexated/Extractors.kt b/Ngefilm/src/main/kotlin/com/hexated/Extractors.kt deleted file mode 100644 index 9730276b..00000000 --- a/Ngefilm/src/main/kotlin/com/hexated/Extractors.kt +++ /dev/null @@ -1,149 +0,0 @@ -package com.hexated - -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.extractors.DoodLaExtractor -import com.lagradost.cloudstream3.extractors.Filesim -import com.lagradost.cloudstream3.extractors.StreamSB -import com.lagradost.cloudstream3.utils.AppUtils -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.Qualities -import java.net.URI -import javax.crypto.Cipher -import javax.crypto.SecretKeyFactory -import javax.crypto.spec.IvParameterSpec -import javax.crypto.spec.PBEKeySpec -import javax.crypto.spec.SecretKeySpec - -class Dooood : DoodLaExtractor() { - override var mainUrl = "https://dooood.com" -} - -class Guccihide : Filesim() { - override val name = "Guccihide" - override var mainUrl = "https://guccihide.com" -} - -class Ahvsh : Filesim() { - override val name = "Ahvsh" - override var mainUrl = "https://ahvsh.com" -} - -class Lvturbo : StreamSB() { - override var name = "Lvturbo" - override var mainUrl = "https://lvturbo.com" -} - -class Sbrapid : StreamSB() { - override var name = "Sbrapid" - override var mainUrl = "https://sbrapid.com" -} - -class Sbface : StreamSB() { - override var name = "Sbface" - override var mainUrl = "https://sbface.com" -} - -class Sbsonic : StreamSB() { - override var name = "Sbsonic" - override var mainUrl = "https://sbsonic.com" -} - -object LocalServer { - private const val KEY = "4VqE3#N7zt&HEP^a" - - private fun getBaseUrl(url: String): String { - return URI(url).let { - "${it.scheme}://${it.host}" - } - } - - 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 encData = AppUtils.tryParseJson(base64Decode(master ?: return)) - val decrypt = cryptoAESHandler(encData ?: return, KEY, false) - - val source = Regex(""""?file"?:\s*"([^"]+)""").find(decrypt)?.groupValues?.get(1) - - // required - val 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, - ) - - callback.invoke( - ExtractorLink( - Ngefilm().name, - Ngefilm().name, - source ?: return, - "$mainUrl/", - Qualities.P1080.value, - headers = headers, - isM3u8 = true - ) - ) - - } - - private fun cryptoAESHandler( - data: AESData, - pass: String, - encrypt: Boolean = true - ): String { - val factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512") - val spec = PBEKeySpec( - pass.toCharArray(), - data.salt?.hexToByteArray(), - data.iterations?.toIntOrNull() ?: 1, - 256 - ) - val key = factory.generateSecret(spec) - val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding") - return if (!encrypt) { - cipher.init( - Cipher.DECRYPT_MODE, - SecretKeySpec(key.encoded, "AES"), - IvParameterSpec(data.iv?.hexToByteArray()) - ) - String(cipher.doFinal(base64DecodeArray(data.ciphertext.toString()))) - } else { - cipher.init( - Cipher.ENCRYPT_MODE, - SecretKeySpec(key.encoded, "AES"), - IvParameterSpec(data.iv?.hexToByteArray()) - ) - base64Encode(cipher.doFinal(data.ciphertext?.toByteArray())) - } - } - - private fun String.hexToByteArray(): ByteArray { - check(length % 2 == 0) { "Must have an even length" } - return chunked(2) - .map { it.toInt(16).toByte() } - - .toByteArray() - } - - data class AESData( - @JsonProperty("ciphertext") val ciphertext: String? = null, - @JsonProperty("iv") val iv: String? = null, - @JsonProperty("salt") val salt: String? = null, - @JsonProperty("iterations") val iterations: String? = null, - ) - -} \ No newline at end of file diff --git a/Ngefilm/src/main/kotlin/com/hexated/Ngefilm.kt b/Ngefilm/src/main/kotlin/com/hexated/Ngefilm.kt index 753f59c5..7e330489 100644 --- a/Ngefilm/src/main/kotlin/com/hexated/Ngefilm.kt +++ b/Ngefilm/src/main/kotlin/com/hexated/Ngefilm.kt @@ -19,14 +19,6 @@ class Ngefilm : MainAPI() { TvType.AsianDrama ) - companion object { - private val localServer = arrayOf( - "https://bestx.stream", - "https://chillx.top", - "https://watchx.top", - ) - } - override val mainPage = mainPageOf( "?s&search=advanced&post_type=movie&index&orderby&genre&movieyear&country&quality=" to "Movies Terbaru", "?s=&search=advanced&post_type=tv&index=&orderby=&genre=&movieyear=&country=&quality=" to "Series Terbaru", @@ -128,11 +120,7 @@ class Ngefilm : MainAPI() { document.select("ul.muvipro-player-tabs li a").apmap { server -> val iframe = app.get(fixUrl(server.attr("href"))).document.selectFirst("div.gmr-embed-responsive iframe") ?.attr("src")?.let { fixUrl(it) } ?: return@apmap - if (localServer.any { iframe.startsWith(it) }) { - LocalServer.getUrl(iframe, "$mainUrl/", subtitleCallback, callback) - } else { - loadExtractor(iframe, "$mainUrl/", subtitleCallback, callback) - } + loadExtractor(iframe, "$mainUrl/", subtitleCallback, callback) } return true diff --git a/Ngefilm/src/main/kotlin/com/hexated/NgefilmPlugin.kt b/Ngefilm/src/main/kotlin/com/hexated/NgefilmPlugin.kt index cbf73937..8fc845f8 100644 --- a/Ngefilm/src/main/kotlin/com/hexated/NgefilmPlugin.kt +++ b/Ngefilm/src/main/kotlin/com/hexated/NgefilmPlugin.kt @@ -10,12 +10,5 @@ class NgefilmPlugin: Plugin() { override fun load(context: Context) { // All providers should be added in this manner. Please don't edit the providers list directly. registerMainAPI(Ngefilm()) - registerExtractorAPI(Sbsonic()) - registerExtractorAPI(Sbface()) - registerExtractorAPI(Sbrapid()) - registerExtractorAPI(Lvturbo()) - registerExtractorAPI(Ahvsh()) - registerExtractorAPI(Guccihide()) - registerExtractorAPI(Dooood()) } } \ No newline at end of file diff --git a/OploverzProvider/build.gradle.kts b/OploverzProvider/build.gradle.kts index e71f3970..49f2737c 100644 --- a/OploverzProvider/build.gradle.kts +++ b/OploverzProvider/build.gradle.kts @@ -1,5 +1,5 @@ // use an integer for version numbers -version = 18 +version = 20 cloudstream { @@ -23,5 +23,5 @@ cloudstream { "OVA", ) - iconUrl = "https://www.google.com/s2/favicons?domain=65.108.132.145&sz=%size%" + iconUrl = "https://www.google.com/s2/favicons?domain=oploverz.care&sz=%size%" } diff --git a/OploverzProvider/src/main/kotlin/com/hexated/OploverzProvider.kt b/OploverzProvider/src/main/kotlin/com/hexated/OploverzProvider.kt index 48c51b87..95d5bcab 100644 --- a/OploverzProvider/src/main/kotlin/com/hexated/OploverzProvider.kt +++ b/OploverzProvider/src/main/kotlin/com/hexated/OploverzProvider.kt @@ -1,11 +1,8 @@ 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.Filesim import com.lagradost.cloudstream3.utils.* -import org.jsoup.Jsoup import org.jsoup.nodes.Element class OploverzProvider : MainAPI() { @@ -23,159 +20,120 @@ class OploverzProvider : MainAPI() { companion object { const val acefile = "https://acefile.co" - const val lbx = "https://lbx.to" - const val linkbox = "https://www.linkbox.to" - fun getType(t: String): TvType { - return when { - t.contains("TV") -> TvType.Anime - t.contains("Movie") -> TvType.AnimeMovie - else -> TvType.OVA - } + return if (t.contains("OVA", true) || t.contains("Special")) TvType.OVA + else if (t.contains("Movie", true)) TvType.AnimeMovie + else TvType.Anime } - fun getStatus(t: String): ShowStatus { + fun getStatus(t: String?): ShowStatus { return when (t) { + "Finished Airing" -> ShowStatus.Completed "Completed" -> ShowStatus.Completed + "Currently Airing" -> ShowStatus.Ongoing "Ongoing" -> ShowStatus.Ongoing else -> ShowStatus.Completed } } + } override val mainPage = mainPageOf( - "&status=&type=&order=update" to "Episode Terbaru", - "&status=&type=&order=latest" to "Anime Terbaru", - "&sub=&order=popular" to "Popular Anime", + "update" to "Latest Update", + "latest" to "Latest Added", + "popular" to "Popular Anime", + "rating" to "Top Rated", ) override suspend fun getMainPage( page: Int, request: MainPageRequest ): HomePageResponse { - val document = app.get("$mainUrl/anime/?page=$page${request.data}").document - val home = document.select("article[itemscope=itemscope]").mapNotNull { + val document = app.get("$mainUrl/anime-list/page/$page/?title&order=${request.data}&status&type").document + val home = document.select("div.relat > article").mapNotNull { it.toSearchResult() } return newHomePageResponse(request.name, home) } private fun getProperAnimeLink(uri: String): String { - return if (uri.contains("/anime/")) { uri } else { var title = uri.substringAfter("$mainUrl/") title = when { - (title.contains("-episode")) && !(title.contains("-ova")) -> Regex("(.+)-episode").find( + (title.contains("-episode")) && !(title.contains("-movie")) -> Regex("(.+)-episode").find( title )?.groupValues?.get(1).toString() - (title.contains("-ova")) -> Regex("(.+)-ova").find(title)?.groupValues?.get(1) - .toString() - (title.contains("-movie")) -> Regex("(.+)-subtitle").find(title)?.groupValues?.get(1) - .toString() - else -> Regex("(.+)-subtitle").find(title)?.groupValues?.get(1).toString() - .replace(Regex("-\\d+"), "") + (title.contains("-movie")) -> Regex("(.+)-movie").find(title)?.groupValues?.get( + 1 + ).toString() + else -> title } - - when { - title.contains("overlord") -> { - title = title.replace("s", "season-") - } - title.contains("kaguya-sama") -> { - title = title.replace("s3", "ultra-romantic") - } - } - "$mainUrl/anime/$title" } - } private fun Element.toSearchResult(): AnimeSearchResponse? { - val href = getProperAnimeLink(this.selectFirst("a.tip")!!.attr("href")) - val title = this.selectFirst("h2[itemprop=headline]")?.text()?.trim() ?: return null - val posterUrl = fixUrlNull(this.selectFirst("img")?.attr("src")) - val type = getType(this.selectFirst(".eggtype, .typez")?.text()?.trim().toString()) - + val title = this.selectFirst("div.title")?.text()?.trim() ?: return null + val href = getProperAnimeLink(this.selectFirst("a")!!.attr("href")) + val posterUrl = this.select("img[itemprop=image]").attr("src").toString() + val type = getType(this.select("div.type").text().trim()) + val epNum = + this.selectFirst("span.episode")?.ownText()?.replace(Regex("\\D"), "")?.trim() + ?.toIntOrNull() return newAnimeSearchResponse(title, href, type) { this.posterUrl = posterUrl + addSub(epNum) } + } override suspend fun search(query: String): List { - val link = "$mainUrl/?s=$query" - val document = app.get(link).document - - return document.select("article[itemscope=itemscope]").map { - val title = it.selectFirst(".tt")?.ownText()?.trim().toString() - val poster = fixUrlNull(it.selectFirst("img")?.attr("src")) - val tvType = getType(it.selectFirst(".typez")?.text().toString()) - val href = fixUrl(it.selectFirst("a.tip")!!.attr("href")) - - newAnimeSearchResponse(title, href, tvType) { - this.posterUrl = poster - addDubStatus(dubExist = false, subExist = true) + val anime = mutableListOf() + (1..2).forEach { page -> + val link = "$mainUrl/page/$page/?s=$query" + val document = app.get(link).document + val media = document.select(".site-main.relat > article").mapNotNull { + val title = it.selectFirst("div.title > h2")!!.ownText().trim() + val href = it.selectFirst("a")!!.attr("href") + val posterUrl = it.selectFirst("img")!!.attr("src").toString() + val type = getType(it.select("div.type").text().trim()) + newAnimeSearchResponse(title, href, type) { + this.posterUrl = posterUrl + } } + if(media.isNotEmpty()) anime.addAll(media) } + return anime } override suspend fun load(url: String): LoadResponse { val document = app.get(url).document - val title = document.selectFirst("h1.entry-title")!!.text().trim() - val poster = document.select(".thumb > img").attr("src") - val tags = document.select(".genxed > a").map { it.text() } + val title = document.selectFirst("h1.entry-title")?.text() + ?.replace("Subtitle Indonesia", "")?.trim() ?: "" + val type = document.selectFirst("div.alternati span.type")?.text() ?: "" - val year = Regex("\\d, (\\d*)").find( - document.selectFirst(".info-content > .spe > span > time")!!.text().trim() - )?.groupValues?.get(1).toString().toIntOrNull() - val status = getStatus( - document.select(".info-content > .spe > span:nth-child(1)") - .text().trim().replace("Status: ", "") - ) - val typeCheck = - when (document.select(".info-content > .spe > span:nth-child(5), .info-content > .spe > span") - .text().trim()) { - "OVA" -> "OVA" - "Movie" -> "Movie" - else -> "TV" - } - val description = document.select(".entry-content > p").text().trim() - val trailer = document.selectFirst("a.trailerbutton")?.attr("href") - - val episodes = document.select(".eplister > ul > li").map { - val link = fixUrl(it.select("a").attr("href")) - val name = it.select(".epl-title").text() - val episode = Regex("Episode\\s?(\\d+[.,]?\\d*)").find(name)?.groupValues?.getOrNull(1)?.toIntOrNull() - Episode(link, name, episode = episode) + val episodes = document.select("div.lstepsiode.listeps ul li").mapNotNull { + val header = it.selectFirst("a") ?: return@mapNotNull null + val episode = header.text().trim().toIntOrNull() + val link = fixUrl(header.attr("href")) + Episode(link, header.text(), episode = episode) }.reversed() - val recommendations = - document.select(".listupd > article[itemscope=itemscope]").mapNotNull { rec -> - val epTitle = rec.selectFirst(".tt")!!.ownText().trim() - val epPoster = rec.selectFirst("img")!!.attr("src") - val epType = getType(rec.selectFirst(".typez")?.text().toString()) - val epHref = fixUrl(rec.selectFirst("a.tip")!!.attr("href")) - - newAnimeSearchResponse(epTitle, epHref, epType) { - this.posterUrl = epPoster - addDubStatus(dubExist = false, subExist = true) - } - } - - return newAnimeLoadResponse(title, url, getType(typeCheck)) { - engName = title - posterUrl = poster - this.year = year + return newAnimeLoadResponse(title, url, getType(type)) { + posterUrl = document.selectFirst("div.thumb > img")?.attr("src") + this.year = document.selectFirst("div.alternati a")?.text()?.filter { it.isDigit() }?.toIntOrNull() addEpisodes(DubStatus.Subbed, episodes) - showStatus = status - plot = description - this.tags = tags - this.recommendations = recommendations - addTrailer(trailer) + showStatus = + getStatus( + document.selectFirst("div.alternati span:nth-child(2)")?.text()?.trim() + ) + plot = document.selectFirst("div.entry-content > p")?.text()?.trim() + this.tags = + document.select("div.genre-info a").map { it.text() } } - } override suspend fun loadLinks( @@ -184,106 +142,81 @@ class OploverzProvider : MainAPI() { subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit ): Boolean { - val document = app.get(data).document - val sources = mutableListOf>() - val streamingSources = document.select(".mobius > .mirror > option").mapNotNull { - "" to fixUrl(Jsoup.parse(base64Decode(it.attr("value"))).select("iframe").attr("src")) - } - if (streamingSources.isNotEmpty()) sources.addAll(streamingSources) - val downloadSources = - document.select("div.mctnx div.soraurlx").mapNotNull { item -> - item.select("a").map { item.select("strong").text() to it.attr("href") } - }.flatten() - if (downloadSources.isNotEmpty()) sources.addAll(downloadSources) - sources.filter { it.second.startsWith("https") }. - apmap { (quality, source) -> - val video = fixedIframe(source) - val videoQuality = getQualityFromName(quality) - if(video.endsWith(".mp4") || video.endsWith(".mkv")) { - callback.invoke( - ExtractorLink( - "Direct", - "Direct", - video, - "", - videoQuality - ) - ) - } else { - loadExtractor(video, data, subtitleCallback) { link -> - callback.invoke( - ExtractorLink( - link.name, - link.name, - link.url, - link.referer, - videoQuality, - link.isM3u8, - link.headers, - link.extractorData - ) - ) + val document = app.get(data).document + + argamap( + { + document.select("div#server ul li div").apmap { + val dataPost = it.attr("data-post") + val dataNume = it.attr("data-nume") + val dataType = it.attr("data-type") + + val iframe = app.post( + url = "$mainUrl/wp-admin/admin-ajax.php", + data = mapOf( + "action" to "player_ajax", + "post" to dataPost, + "nume" to dataNume, + "type" to dataType + ), + referer = data, + headers = mapOf("X-Requested-With" to "XMLHttpRequest") + ).document.select("iframe").attr("src") + + loadExtractor(fixedIframe(iframe), "$mainUrl/", subtitleCallback, callback) + + } + }, + { + document.select("div#download tr").map { el -> + el.select("a").apmap { + loadFixedExtractor(fixedIframe(it.attr("href")), el.select("strong").text(), "$mainUrl/", subtitleCallback, callback) + } } } - } + ) return true } - private suspend fun fixedIframe(url: String): String { - val id = Regex("""(?:/f/|/file/)(\w+)""").find(url)?.groupValues?.getOrNull(1) - return when { - url.startsWith(acefile) -> "$acefile/player/$id" - url.startsWith(lbx) -> { - val itemId = app.get("$linkbox/api/file/share_out_list/?sortField=utime&sortAsc=0&pageNo=1&pageSize=50&shareToken=$id&scene=singleItem&needTpInfo=1&lan=en").parsedSafe()?.data?.itemId - "$linkbox/a/f/$itemId" - } - else -> url - } - } - - data class RList( - @JsonProperty("url") val url: String, - @JsonProperty("resolution") val resolution: String?, - ) - - data class Data( - @JsonProperty("rList") val rList: List? = arrayListOf(), - @JsonProperty("itemId") val itemId: String? = null, - ) - - data class Responses( - @JsonProperty("data") val data: Data?, - ) - -} - -class Streamhide : Filesim() { - override val mainUrl = "https://streamhide.to" - override val name = "Streamhide" -} - -open class Pixeldrain : ExtractorApi() { - override val name = "Pixeldrain" - override val mainUrl = "https://pixeldrain.com" - override val requiresReferer = false - override suspend fun getUrl( + private suspend fun loadFixedExtractor( url: String, - referer: String?, + name: String, + referer: String? = null, subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit ) { - val mId = Regex("/([ul]/[\\da-zA-Z\\-]+)").find(url)?.groupValues?.get(1)?.split("/") - callback.invoke( - ExtractorLink( - this.name, - this.name, - "$mainUrl/api/file/${mId?.last() ?: return}?download", - url, - Qualities.Unknown.value, + loadExtractor(url, referer, subtitleCallback) { link -> + callback.invoke( + ExtractorLink( + link.name, + link.name, + link.url, + link.referer, + name.fixQuality(), + link.isM3u8, + link.headers, + link.extractorData + ) ) - ) + } + } + + private fun String.fixQuality() : Int { + return when(this) { + "MP4HD" -> Qualities.P720.value + "FULLHD" -> Qualities.P1080.value + else -> Regex("(\\d{3,4})p").find(this)?.groupValues?.get(1)?.toIntOrNull() ?: Qualities.Unknown.value + } + } + + private fun fixedIframe(url: String): String { + val id = Regex("""(?:/f/|/file/)(\w+)""").find(url)?.groupValues?.getOrNull(1) + return when { + url.startsWith(acefile) -> "${acefile}/player/$id" + else -> fixUrl(url) + } } } diff --git a/OploverzProvider/src/main/kotlin/com/hexated/OploverzProviderPlugin.kt b/OploverzProvider/src/main/kotlin/com/hexated/OploverzProviderPlugin.kt index 4f313881..157869bd 100644 --- a/OploverzProvider/src/main/kotlin/com/hexated/OploverzProviderPlugin.kt +++ b/OploverzProvider/src/main/kotlin/com/hexated/OploverzProviderPlugin.kt @@ -10,7 +10,5 @@ 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(Streamhide()) - registerExtractorAPI(Pixeldrain()) } } \ No newline at end of file diff --git a/PhimmoichillProvider/build.gradle.kts b/PhimmoichillProvider/build.gradle.kts index 091dbc67..0e836afc 100644 --- a/PhimmoichillProvider/build.gradle.kts +++ b/PhimmoichillProvider/build.gradle.kts @@ -1,5 +1,5 @@ // use an integer for version numbers -version = 4 +version = 5 cloudstream { diff --git a/PhimmoichillProvider/src/main/kotlin/com/hexated/PhimmoichillProvider.kt b/PhimmoichillProvider/src/main/kotlin/com/hexated/PhimmoichillProvider.kt index 75e214ce..79823186 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://phimmoichilla.net" + override var mainUrl = "https://phimmoichilld.net" override var name = "Phimmoichill" override val hasMainPage = true override var lang = "vi" @@ -26,9 +26,11 @@ class PhimmoichillProvider : MainAPI() { "$mainUrl/list/phim-le/page-" to "Phim Lẻ", "$mainUrl/list/phim-bo/page-" to "Phim Bộ", "$mainUrl/genre/phim-hoat-hinh/page-" to "Phim Hoạt Hình", + "$mainUrl/genre/phim-anime/page-" to "Phim Anime", "$mainUrl/country/phim-han-quoc/page-" to "Phim Hàn Quốc", "$mainUrl/country/phim-trung-quoc/page-" to "Phim Trung Quốc", "$mainUrl/country/phim-thai-lan/page-" to "Phim Thái Lan", + "$mainUrl/genre/phim-sap-chieu/page-" to "Phim Sắp Chiếu", ) override suspend fun getMainPage( @@ -64,6 +66,10 @@ class PhimmoichillProvider : MainAPI() { this.posterUrl = posterUrl addSub(episode) } + } else if (temp.contains(Regex("Trailer"))) { + newMovieSearchResponse(title, href, TvType.Movie) { + this.posterUrl = posterUrl + } } else { val quality = temp.replace(Regex("(-.*)|(\\|.*)|(?i)(VietSub.*)|(?i)(Thuyết.*)"), "").trim() @@ -83,21 +89,20 @@ class PhimmoichillProvider : MainAPI() { } } - override suspend fun load(url: String): LoadResponse { + override suspend fun load( url: String ): LoadResponse { val document = app.get(url).document val title = document.selectFirst("h1[itemprop=name]")?.text()?.trim().toString() val link = document.select("ul.list-button li:last-child a").attr("href") val poster = document.selectFirst("div.image img[itemprop=image]")?.attr("src") - val tags = document.select("ul.entry-meta.block-film li:nth-child(4) a").map { it.text() } + val tags = document.select("ul.entry-meta.block-film li:nth-child(4) a").map { it.text()!!.substringAfter("Phim") } val year = document.select("ul.entry-meta.block-film li:nth-child(2) a").text().trim() .toIntOrNull() val tvType = if (document.select("div.latest-episode").isNotEmpty() ) TvType.TvSeries else TvType.Movie - val description = document.select("div#film-content").text().trim() - val trailer = - document.select("div#trailer script").last()?.data()?.substringAfter("file: \"") - ?.substringBefore("\",") + val description = document.select("div#film-content").text().substringAfter("Full HD Vietsub Thuyết Minh").substringBefore("@phimmoi").trim() + val trailer = document.select("body script") + .find { it.data().contains("youtube.com") }?.data()?.substringAfterLast("file: \"")?.substringBefore("\",") val rating = document.select("ul.entry-meta.block-film li:nth-child(7) span").text().toRatingInt() val actors = document.select("ul.entry-meta.block-film li:last-child a").map { it.text() } @@ -143,7 +148,7 @@ class PhimmoichillProvider : MainAPI() { } } } - + override suspend fun loadLinks( data: String, isCasting: Boolean, @@ -156,8 +161,7 @@ class PhimmoichillProvider : MainAPI() { .find { it.data().contains("filmInfo.episodeID =") }?.data()?.let { script -> val id = script.substringAfter("filmInfo.episodeID = parseInt('") app.post( - // Not mainUrl - url = "https://phimmoichills.net/pmplayer.php", + url = "${this.mainUrl}/chillsplayer.php", data = mapOf("qcao" to id, "sv" to "0"), referer = data, headers = mapOf( @@ -171,6 +175,7 @@ class PhimmoichillProvider : MainAPI() { listOf( Pair("https://so-trym.topphimmoi.org/raw/$key/index.m3u8", "PMFAST"), Pair("https://dash.megacdn.xyz/raw/$key/index.m3u8", "PMHLS"), + Pair("https://so-trym.phimchill.net/dash/$key/index.m3u8", "PMPRO"), Pair("https://dash.megacdn.xyz/dast/$key/index.m3u8", "PMBK") ).apmap { (link, source) -> safeApiCall { diff --git a/Samehadaku/build.gradle.kts b/Samehadaku/build.gradle.kts index 4e5c21b1..a262ac5b 100644 --- a/Samehadaku/build.gradle.kts +++ b/Samehadaku/build.gradle.kts @@ -1,5 +1,5 @@ // use an integer for version numbers -version = 10 +version = 11 cloudstream { diff --git a/Samehadaku/src/main/kotlin/com/hexated/Extractors.kt b/Samehadaku/src/main/kotlin/com/hexated/Extractors.kt deleted file mode 100644 index f0b6db8f..00000000 --- a/Samehadaku/src/main/kotlin/com/hexated/Extractors.kt +++ /dev/null @@ -1,43 +0,0 @@ -package com.hexated - -import com.lagradost.cloudstream3.SubtitleFile -import com.lagradost.cloudstream3.app -import com.lagradost.cloudstream3.extractors.XStreamCdn -import com.lagradost.cloudstream3.utils.ExtractorApi -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.Qualities -import java.net.URI - -class Suzihaza: XStreamCdn() { - override val name: String = "Suzihaza" - override val mainUrl: String = "https://suzihaza.com" -} - -open class Wibufile : ExtractorApi() { - override val name: String = "Wibufile" - override val mainUrl: String = "https://wibufile.com" - override val requiresReferer = false - - override suspend fun getUrl( - url: String, - referer: String?, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ) { - val res = app.get(url).text - val video = Regex("src: ['\"](.*?)['\"]").find(res)?.groupValues?.get(1) - - callback.invoke( - ExtractorLink( - name, - name, - video ?: return, - "$mainUrl/", - Qualities.Unknown.value, - URI(url).path.endsWith(".m3u8") - ) - ) - - } - -} \ No newline at end of file diff --git a/Samehadaku/src/main/kotlin/com/hexated/SamehadakuPlugin.kt b/Samehadaku/src/main/kotlin/com/hexated/SamehadakuPlugin.kt index 65e90137..307477b6 100644 --- a/Samehadaku/src/main/kotlin/com/hexated/SamehadakuPlugin.kt +++ b/Samehadaku/src/main/kotlin/com/hexated/SamehadakuPlugin.kt @@ -10,7 +10,5 @@ class SamehadakuPlugin: Plugin() { override fun load(context: Context) { // All providers should be added in this manner. Please don't edit the providers list directly. registerMainAPI(Samehadaku()) - registerExtractorAPI(Suzihaza()) - registerExtractorAPI(Wibufile()) } } \ No newline at end of file diff --git a/SoraStream/build.gradle.kts b/SoraStream/build.gradle.kts index 77759bb8..c770eba3 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 = 141 +version = 144 android { defaultConfig { diff --git a/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt b/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt index d08175ad..933475ca 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt @@ -831,12 +831,19 @@ object SoraExtractor : SoraStream() { title: String? = null, season: Int? = null, episode: Int? = null, + isAnime: Boolean = false, + lastSeason: Int? = null, subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit ) { - val fixTitle = title?.replace("–", "-") + val slug = title.createSlug() ?: return + val type = when { + isAnime -> "3" + season == null -> "2" + else -> "1" + } val res = app.get( - "$kissKhAPI/api/DramaList/Search?q=$title&type=0", referer = "$kissKhAPI/" + "$kissKhAPI/api/DramaList/Search?q=$title&type=$type", referer = "$kissKhAPI/" ).text.let { tryParseJson>(it) } ?: return @@ -844,17 +851,15 @@ object SoraExtractor : SoraStream() { val (id, contentTitle) = if (res.size == 1) { res.first().id to res.first().title } else { - if (season == null) { - val data = res.find { it.title.equals(fixTitle, true) } - data?.id to data?.title - } else { - val data = res.find { - it.title?.contains( - "$fixTitle", true - ) == true && it.title.contains("Season $season", true) + val data = res.find { + val slugTitle = it.title.createSlug() + when { + season == null -> slugTitle?.equals(slug) == true + lastSeason == 1 -> slugTitle?.contains(slug) == true + else -> slugTitle?.contains(slug) == true && it.title?.contains("Season $season", true) == true } - data?.id to data?.title } + data?.id to data?.title } val resDetail = app.get( @@ -955,7 +960,7 @@ object SoraExtractor : SoraStream() { ) { val res = app.get( "$biliBiliAPI/anime/episodes?id=${aniId ?: return}&source_id=bilibili", - referer = kaguyaBaseUrl + referer = otakuzBaseUrl ) .parsedSafe()?.episodes?.find { it.episodeNumber == episode @@ -964,7 +969,7 @@ object SoraExtractor : SoraStream() { val sources = app.get( "$biliBiliAPI/source?episode_id=${res.sourceEpisodeId}&source_media_id=${res.sourceMediaId}&source_id=${res.sourceId}", - referer = kaguyaBaseUrl + referer = otakuzBaseUrl ) .parsedSafe() @@ -972,7 +977,7 @@ object SoraExtractor : SoraStream() { val quality = app.get( source.file ?: return@apmap null, - referer = kaguyaBaseUrl + referer = otakuzBaseUrl ).document.selectFirst("Representation") ?.attr("height") callback.invoke( @@ -980,7 +985,7 @@ object SoraExtractor : SoraStream() { "BiliBili", "BiliBili", source.file, - kaguyaBaseUrl, + "", quality?.toIntOrNull() ?: Qualities.Unknown.value, isDash = true ) @@ -1008,16 +1013,18 @@ object SoraExtractor : SoraStream() { val animeId = app.get("https://raw.githubusercontent.com/MALSync/MAL-Sync-Backup/master/data/anilist/anime/${aniId ?: return}.json") .parsedSafe()?.pages?.zoro?.keys?.map { it } - + val headers = mapOf( + "X-Requested-With" to "XMLHttpRequest", + ) animeId?.apmap { id -> - val episodeId = app.get("$zoroAPI/ajax/v2/episode/list/${id ?: return@apmap}") + val episodeId = app.get("$zoroAPI/ajax/episode/list/${id ?: return@apmap}", headers = headers) .parsedSafe()?.html?.let { Jsoup.parse(it) }?.select("div.ss-list a")?.find { it.attr("data-number") == "${episode ?: 1}" } ?.attr("data-id") val servers = - app.get("$zoroAPI/ajax/v2/episode/servers?episodeId=${episodeId ?: return@apmap}") + app.get("$zoroAPI/ajax/episode/servers?episodeId=${episodeId ?: return@apmap}", headers = headers) .parsedSafe()?.html?.let { Jsoup.parse(it) } ?.select("div.item.server-item")?.map { Triple( @@ -1029,10 +1036,10 @@ object SoraExtractor : SoraStream() { servers?.apmap servers@{ server -> val iframe = - app.get("$zoroAPI/ajax/v2/episode/sources?id=${server.second ?: return@servers}") + app.get("$zoroAPI/ajax/episode/sources?id=${server.second ?: return@servers}", headers = headers) .parsedSafe()?.link ?: return@servers val audio = if (server.third == "sub") "Raw" else "English Dub" - if (server.first == "Vidstreaming" || server.first == "Vidcloud") { + if (server.first.contains(Regex("Vidstreaming|MegaCloud|Vidcloud"))) { extractRabbitStream( "${server.first} [$audio]", iframe, @@ -1255,20 +1262,13 @@ object SoraExtractor : SoraStream() { extractMirrorUHD(bitLink, base) } - val tags = - Regex("\\d{3,4}[Pp]\\.?(.*?)\\[").find(quality)?.groupValues?.getOrNull(1) - ?.replace(".", " ")?.trim() - ?: "" - val qualities = - Regex("(\\d{3,4})[Pp]").find(quality)?.groupValues?.getOrNull(1)?.toIntOrNull() - ?: Qualities.Unknown.value - val size = - Regex("(?i)\\[(\\S+\\s?(gb|mb))[]/]").find(quality)?.groupValues?.getOrNull(1) - ?.let { "[$it]" } ?: quality + val tags = getUhdTags(quality) + val qualities = getIndexQuality(quality) + val size = getIndexSize(quality) callback.invoke( ExtractorLink( "UHDMovies", - "UHDMovies $tags $size", + "UHDMovies $tags [$size]", downloadLink ?: return@apmap, "", qualities @@ -1280,6 +1280,72 @@ object SoraExtractor : SoraStream() { } + suspend fun invokePobmovies( + title: String? = null, + year: Int? = null, + callback: (ExtractorLink) -> Unit + ) { + val detailDoc = app.get("$pobmoviesAPI/${title.createSlug()}-$year").document + val iframeList = detailDoc.select("div.entry-content p").map { it } + .filter { it.text().filterIframe(year = year, title = title) }.mapNotNull { + it.text() to it.nextElementSibling()?.select("a")?.attr("href") + }.filter { it.second?.contains(Regex("(https:)|(http:)")) == true } + + val sources = mutableListOf>() + if (iframeList.any { + it.first.contains( + "2160p", + true + ) + }) { + sources.addAll(iframeList.filter { + it.first.contains( + "2160p", + true + ) + }) + sources.add(iframeList.first { + it.first.contains( + "1080p", + true + ) + }) + } else { + sources.addAll(iframeList.filter { it.first.contains("1080p", true) }) + } + + sources.apmap { (name, link) -> + if (link.isNullOrEmpty()) return@apmap + val videoLink = when { + link.contains("gdtot") -> { + val gdBotLink = extractGdbot(link) + extractGdflix(gdBotLink ?: return@apmap) + } + link.contains("gdflix") -> { + extractGdflix(link) + } + else -> { + return@apmap + } + } + + val tags = getUhdTags(name) + val qualities = getIndexQuality(name) + val size = getIndexSize(name) + callback.invoke( + ExtractorLink( + "Pobmovies", + "Pobmovies $tags [${size}]", + videoLink ?: return@apmap, + "", + qualities + ) + ) + + } + + } + suspend fun invokeFwatayako( imdbId: String? = null, season: Int? = null, @@ -3033,6 +3099,118 @@ object SoraExtractor : SoraStream() { } + suspend fun invokeEmovies( + title: String? = null, + year: Int? = null, + season: Int? = null, + episode: Int? = null, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit, + ) { + val slug = title.createSlug() + val url = if (season == null) { + "$emoviesAPI/watch-$slug-$year-1080p-hd-online-free/watching.html" + } else { + val first = "$emoviesAPI/watch-$slug-season-$season-$year-1080p-hd-online-free.html" + val second = "$emoviesAPI/watch-$slug-$year-1080p-hd-online-free.html" + if (app.get(first).isSuccessful) first else second + } + + val res = app.get(url).document + val id = (if (season == null) { + res.selectFirst("select#selectServer option[sv=oserver]")?.attr("value") + } else { + res.select("div.le-server a").find { + val num = + Regex("Episode (\\d+)").find(it.text())?.groupValues?.get(1)?.toIntOrNull() + num == episode + }?.attr("href") + })?.substringAfter("id=")?.substringBefore("&") + + val server = + app.get( + "$emoviesAPI/ajax/v4_get_sources?s=oserver&id=${id ?: return}&_=${APIHolder.unixTimeMS}", + headers = mapOf( + "X-Requested-With" to "XMLHttpRequest" + ) + ).parsedSafe()?.value + + val script = app.get(server ?: return, referer = "$emoviesAPI/").document.selectFirst("script:containsData(sources:)")?.data() ?: return + val sources = Regex("sources:\\s*\\[(.*)],").find(script)?.groupValues?.get(1)?.let { + tryParseJson>("[$it]") + } + val tracks = Regex("tracks:\\s*\\[(.*)],").find(script)?.groupValues?.get(1)?.let { + tryParseJson>("[$it]") + } + + sources?.map { source -> + M3u8Helper.generateM3u8( + "Emovies", + source.file ?: return@map, + "https://embed.vodstream.xyz/" + ).forEach(callback) + } + + tracks?.map { track -> + subtitleCallback.invoke( + SubtitleFile( + track.label ?: "", + track.file ?: return@map, + ) + ) + } + + + } + + suspend fun invokeFourCartoon( + title: String? = null, + year: Int? = null, + season: Int? = null, + episode: Int? = null, + callback: (ExtractorLink) -> Unit + ) { + val fixTitle = title.createSlug() + val headers = mapOf( + "X-Requested-With" to "XMLHttpRequest" + ) + val url = if (season == null) { + "$fourCartoonAPI/movies/$fixTitle-$year" + } else { + "$fourCartoonAPI/episode/$fixTitle-season-$season-episode-$episode" + } + + val document = app.get(url).document + val id = document.selectFirst("input[name=idpost]")?.attr("value") + val server = app.get( + "$fourCartoonAPI/ajax-get-link-stream/?server=streamango&filmId=${id ?: return}", + headers = headers + ).text + val hash = + getAndUnpack(app.get(server, referer = fourCartoonAPI).text).substringAfter("(\"") + .substringBefore("\",") + val iframeUrl = getBaseUrl(server) + val source = app.post( + "$iframeUrl/player/index.php?data=$hash&do=getVideo", data = mapOf( + "hast" to hash, + "r" to "$fourCartoonAPI/", + ), + headers = headers + ).parsedSafe()?.videoSource + + callback.invoke( + ExtractorLink( + "4Cartoon", + "4Cartoon", + source ?: return, + "$iframeUrl/", + Qualities.P720.value, + true, + ) + ) + + } + } diff --git a/SoraStream/src/main/kotlin/com/hexated/SoraParser.kt b/SoraStream/src/main/kotlin/com/hexated/SoraParser.kt index 7c3345a0..9646b846 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraParser.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraParser.kt @@ -487,3 +487,20 @@ data class EpisodeVo( data class DumpMediaDetail( @JsonProperty("episodeVo") val episodeVo: ArrayList? = arrayListOf(), ) + +data class EMovieServer( + @JsonProperty("value") val value: String? = null, +) + +data class EMovieSources( + @JsonProperty("file") val file: String? = null, +) + +data class EMovieTraks( + @JsonProperty("file") val file: String? = null, + @JsonProperty("label") val label: String? = null, +) + +data class FourCartoonSources( + @JsonProperty("videoSource") val videoSource: String? = null, +) diff --git a/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt b/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt index 0fe1ed27..e0744b10 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt @@ -49,6 +49,9 @@ import com.hexated.SoraExtractor.invokeShinobiMovies import com.hexated.SoraExtractor.invokeShivamhw import com.hexated.SoraExtractor.invokeSmashyStream import com.hexated.SoraExtractor.invokeDumpStream +import com.hexated.SoraExtractor.invokeEmovies +import com.hexated.SoraExtractor.invokeFourCartoon +import com.hexated.SoraExtractor.invokePobmovies import com.hexated.SoraExtractor.invokeTvMovies import com.hexated.SoraExtractor.invokeUhdmovies import com.hexated.SoraExtractor.invokeVitoenMovies @@ -97,11 +100,11 @@ open class SoraStream : TmdbProvider() { const val filmxyAPI = "https://www.filmxy.vip" const val kimcartoonAPI = "https://kimcartoon.li" const val xMovieAPI = "https://xemovies.to" - const val zoroAPI = "https://sanji.to" + const val zoroAPI = "https://kaido.to" 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.cc" + const val uhdmoviesAPI = "https://uhdmovies.life" const val fwatayakoAPI = "https://5100.svetacdn.in" const val gMoviesAPI = "https://gdrivemovies.xyz" const val fdMoviesAPI = "https://freedrivemovie.lol" @@ -118,7 +121,7 @@ open class SoraStream : TmdbProvider() { const val watchSomuchAPI = "https://watchsomuch.tv" // sub only val gomoviesAPI = base64DecodeAPI("bQ==Y28=ZS4=aW4=bmw=LW8=ZXM=dmk=bW8=Z28=Ly8=czo=dHA=aHQ=") const val ask4MoviesAPI = "https://ask4movie.net" - const val biliBiliAPI = "https://api-vn.kaguya.app/server" + const val biliBiliAPI = "https://api-vn.otakuz.live/server" const val watchOnlineAPI = "https://watchonline.ag" const val nineTvAPI = "https://api.9animetv.live" const val putlockerAPI = "https://ww7.putlocker.vip" @@ -127,6 +130,9 @@ open class SoraStream : TmdbProvider() { const val gokuAPI = "https://goku.sx" const val ridomoviesAPI = "https://ridomovies.pw" const val navyAPI = "https://navy-issue-i-239.site" + const val emoviesAPI = "https://emovies.si" + const val pobmoviesAPI = "https://pobmovies.cam" + const val fourCartoonAPI = "https://4cartoon.net" // INDEX SITE const val blackMoviesAPI = "https://dl.blacklistedbois.workers.dev/0:" @@ -261,8 +267,7 @@ open class SoraStream : TmdbProvider() { val year = releaseDate?.split("-")?.first()?.toIntOrNull() val rating = res.vote_average.toString().toRatingInt() val genres = res.genres?.mapNotNull { it.name } - val isAnime = - genres?.contains("Animation") == true && (res.original_language == "zh" || res.original_language == "ja") + val isAnime = genres?.contains("Animation") == true && (res.original_language == "zh" || res.original_language == "ja") val keywords = res.keywords?.results?.mapNotNull { it.name }.orEmpty() .ifEmpty { res.keywords?.keywords?.mapNotNull { it.name } } @@ -302,7 +307,7 @@ open class SoraStream : TmdbProvider() { epsTitle = eps.name, jpTitle = res.alternative_titles?.results?.find { it.iso_3166_1 == "JP" }?.title, date = season.airDate, - airedDate = res.releaseDate ?: res.firstAirDate + airedDate = res.releaseDate ?: res.firstAirDate, ).toJson(), name = eps.name + if(isUpcoming(eps.airDate)) " - [UPCOMING]" else "", season = eps.seasonNumber, @@ -346,7 +351,7 @@ open class SoraStream : TmdbProvider() { orgTitle = orgTitle, isAnime = isAnime, jpTitle = res.alternative_titles?.results?.find { it.iso_3166_1 == "JP" }?.title, - airedDate = res.releaseDate ?: res.firstAirDate + airedDate = res.releaseDate ?: res.firstAirDate, ).toJson(), ) { this.posterUrl = poster @@ -493,18 +498,18 @@ open class SoraStream : TmdbProvider() { ) }, { - invokeKimcartoon(res.title, res.season, res.episode, subtitleCallback, callback) - }, - { - invokeXmovies( - res.title, - res.year, - res.season, - res.episode, - subtitleCallback, - callback - ) + if(!res.isAnime) invokeKimcartoon(res.title, res.season, res.episode, subtitleCallback, callback) }, +// { +// invokeXmovies( +// res.title, +// res.year, +// res.season, +// res.episode, +// subtitleCallback, +// callback +// ) +// }, { if (!res.isAnime) invokeFmovies( res.title, @@ -516,7 +521,7 @@ open class SoraStream : TmdbProvider() { ) }, { - invokeKisskh(res.title, res.season, res.episode, subtitleCallback, callback) + invokeKisskh(res.title, res.season, res.episode, res.isAnime, res.lastSeason, subtitleCallback, callback) }, { invokeLing( @@ -803,6 +808,15 @@ open class SoraStream : TmdbProvider() { { invokeNavy(res.imdbId, res.season, res.episode, callback) }, + { + if (!res.isAnime) invokeEmovies(res.title, res.year, res.season, res.episode, subtitleCallback, callback) + }, + { + if(!res.isAnime && res.season == null) invokePobmovies(res.title, res.year, callback) + }, + { + if(!res.isAnime) invokeFourCartoon(res.title, res.year, res.season, res.episode, callback) + } ) return true diff --git a/SoraStream/src/main/kotlin/com/hexated/SoraStreamLite.kt b/SoraStream/src/main/kotlin/com/hexated/SoraStreamLite.kt index 46859759..dec99931 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraStreamLite.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraStreamLite.kt @@ -27,6 +27,8 @@ import com.hexated.SoraExtractor.invokeRidomovies import com.hexated.SoraExtractor.invokeSeries9 import com.hexated.SoraExtractor.invokeSmashyStream import com.hexated.SoraExtractor.invokeDumpStream +import com.hexated.SoraExtractor.invokeEmovies +import com.hexated.SoraExtractor.invokeFourCartoon import com.hexated.SoraExtractor.invokeVidSrc import com.hexated.SoraExtractor.invokeWatchOnline import com.hexated.SoraExtractor.invokeWatchsomuch @@ -186,7 +188,7 @@ class SoraStreamLite : SoraStream() { ) }, { - invokeKimcartoon(res.title, res.season, res.episode, subtitleCallback, callback) + if(!res.isAnime) invokeKimcartoon(res.title, res.season, res.episode, subtitleCallback, callback) }, { invokeSmashyStream( @@ -198,16 +200,16 @@ class SoraStreamLite : SoraStream() { callback ) }, - { - invokeXmovies( - res.title, - res.year, - res.season, - res.episode, - subtitleCallback, - callback - ) - }, +// { +// invokeXmovies( +// res.title, +// res.year, +// res.season, +// res.episode, +// subtitleCallback, +// callback +// ) +// }, { if (!res.isAnime) invokeFmovies( res.title, @@ -219,7 +221,7 @@ class SoraStreamLite : SoraStream() { ) }, { - invokeKisskh(res.title, res.season, res.episode, subtitleCallback, callback) + invokeKisskh(res.title, res.season, res.episode, res.isAnime, res.lastSeason, subtitleCallback, callback) }, { invokeLing( @@ -287,6 +289,25 @@ class SoraStreamLite : SoraStream() { res.year, callback ) + }, + { + if (!res.isAnime) invokeEmovies( + res.title, + res.year, + res.season, + res.episode, + subtitleCallback, + callback + ) + }, + { + if(!res.isAnime) invokeFourCartoon( + res.title, + res.year, + res.season, + res.episode, + callback + ) } ) diff --git a/SoraStream/src/main/kotlin/com/hexated/SoraUtils.kt b/SoraStream/src/main/kotlin/com/hexated/SoraUtils.kt index d3ce46d8..524470c6 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraUtils.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraUtils.kt @@ -2,7 +2,6 @@ package com.hexated import android.util.Base64 import com.fasterxml.jackson.annotation.JsonProperty -import com.hexated.DumpUtils.createHeaders import com.hexated.DumpUtils.queryApi import com.hexated.SoraStream.Companion.anilistAPI import com.hexated.SoraStream.Companion.base64DecodeAPI @@ -51,7 +50,7 @@ import kotlin.math.min val bflixChipperKey = base64DecodeAPI("Yjc=ejM=TzA=YTk=WHE=WnU=bXU=RFo=") const val bflixKey = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" -const val kaguyaBaseUrl = "https://kaguya.app/" +const val otakuzBaseUrl = "https://otakuz.live/" val soraHeaders = mapOf( "lang" to "en", "versioncode" to "33", @@ -111,7 +110,12 @@ data class FilmxyCookies( val wSec: String? = null, ) -fun String.filterIframe(seasonNum: Int?, lastSeason: Int?, year: Int?, title: String?): Boolean { +fun String.filterIframe( + seasonNum: Int? = null, + lastSeason: Int? = null, + year: Int?, + title: String? +): Boolean { val slug = title.createSlug() val dotSlug = slug?.replace("-", ".") val spaceSlug = slug?.replace("-", " ") @@ -271,8 +275,8 @@ suspend fun extractDrivebot(url: String): String? { suspend fun extractGdflix(url: String): String? { val iframeGdflix = - app.get(url).document.selectFirst("li.flex.flex-col.py-6 a:contains(GDFlix Direct)") - ?.attr("href") ?: return null + if (!url.contains("gdflix")) app.get(url).document.selectFirst("li.flex.flex-col.py-6 a:contains(GDFlix Direct)") + ?.attr("href") ?: return null else url val base = getBaseUrl(iframeGdflix) val req = app.get(iframeGdflix).document.selectFirst("script:containsData(replace)")?.data() @@ -586,7 +590,10 @@ suspend fun getDumpIdAndType(title: String?, year: Int?, season: Int?): Pair { it.name?.contains(Regex("(?i)$title\\s?($season|${season.toRomanNumeral()}|Season\\s$season)")) == true && it.releaseTime == "$year" && it.domainType == 1 @@ -744,7 +751,11 @@ suspend fun bypassTechmny(url: String): String? { val thirdPage = secondPage.getNextTechPage().text val goToken = thirdPage.substringAfter("?go=").substringBefore("\"") val tokenUrl = "$postUrl?go=$goToken" - val headers = mapOf("Cookie" to "$goToken=${secondPage.select("form#landing input[name=_wp_http2]").attr("value")}") + val headers = mapOf( + "Cookie" to "$goToken=${ + secondPage.select("form#landing input[name=_wp_http2]").attr("value") + }" + ) Pair(tokenUrl, headers) } val driveUrl = @@ -1279,6 +1290,12 @@ fun String.getFileSize(): Float? { } } +fun getUhdTags(str: String?): String { + return Regex("\\d{3,4}[Pp]\\.?(.*?)\\[").find(str ?: "")?.groupValues?.getOrNull(1) + ?.replace(".", " ")?.trim() + ?: str ?: "" +} + fun getIndexQualityTags(str: String?, fullTag: Boolean = false): String { return if (fullTag) Regex("(?i)(.*)\\.(?:mkv|mp4|avi)").find(str ?: "")?.groupValues?.get(1) ?.trim() ?: str ?: "" else Regex("(?i)\\d{3,4}[pP]\\.?(.*?)\\.(mkv|mp4|avi)").find( @@ -1486,12 +1503,13 @@ fun getBaseUrl(url: String): String { } } -fun isUpcoming(dateString: String?) : Boolean { - if(dateString == null) return false +fun isUpcoming(dateString: String?): Boolean { + if (dateString == null) return false val format = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()) val dateTime = format.parse(dateString)?.time ?: return false return unixTimeMS < dateTime } + fun decode(input: String): String = URLDecoder.decode(input, "utf-8") fun encode(input: String): String = URLEncoder.encode(input, "utf-8").replace("+", "%20") @@ -1761,12 +1779,16 @@ object RabbitStream { ioSafe { app.get("$extractorData&t=${generateTimeStamp()}&sid=${pollingData.sid}") } } } - val getSourcesUrl = "${ - mainIframeUrl.replace( + val mainIframeAjax = mainIframeUrl.let { + if(it.contains("/embed-2/e-1")) it.replace( + "/embed-2/e-1", + "/embed-2/ajax/e-1" + ) else it.replace( "/embed", "/ajax/embed" ) - }/getSources?id=$mainIframeId${sid?.let { "$&sId=$it" } ?: ""}" + } + val getSourcesUrl = "$mainIframeAjax/getSources?id=$mainIframeId${sid?.let { "$&sId=$it" } ?: ""}" val response = app.get( getSourcesUrl, referer = mainUrl, @@ -1929,7 +1951,7 @@ object RabbitStream { } suspend fun getZoroKey(): String { - return app.get("https://raw.githubusercontent.com/enimax-anime/key/e6/key.txt").text + return app.get("https://raw.githubusercontent.com/enimax-anime/key/e0/key.txt").text } private inline fun decryptMapped(input: String, key: String): T? { @@ -2021,8 +2043,9 @@ object DumpUtils { return app.custom( method, url, - requestBody = if(method == "POST") params.toJson().toRequestBody(RequestBodyTypes.JSON.toMediaTypeOrNull()) else null, - params = if(method == "GET") params else emptyMap(), + requestBody = if (method == "POST") params.toJson() + .toRequestBody(RequestBodyTypes.JSON.toMediaTypeOrNull()) else null, + params = if (method == "GET") params else emptyMap(), headers = createHeaders(params) ).parsedSafe>()?.get("data").let { cryptoHandler( @@ -2062,7 +2085,8 @@ object DumpUtils { } private fun getAesKey(): String? { - val publicKey = RSAEncryptionHelper.getPublicKeyFromString(BuildConfig.DUMP_KEY) ?: return null + val publicKey = + RSAEncryptionHelper.getPublicKeyFromString(BuildConfig.DUMP_KEY) ?: return null return RSAEncryptionHelper.encryptText(deviceId, publicKey) } diff --git a/StremioX/build.gradle.kts b/StremioX/build.gradle.kts index 7edaa107..5e6b7146 100644 --- a/StremioX/build.gradle.kts +++ b/StremioX/build.gradle.kts @@ -6,7 +6,7 @@ cloudstream { language = "en" // All of these properties are optional, you can safely remove them - description = "- StremioX allows you to use stream addons \n- StremioC allows you to use catalog addons \n Requires Setup" + description = "[!] Requires Setup \n- StremioX allows you to use stream addons \n- StremioC allows you to use catalog addons" authors = listOf("Hexated") /**