From d6777e5ea312ecd8aa908d9ba362d6fcab63e7d5 Mon Sep 17 00:00:00 2001 From: hexated Date: Sat, 10 Jun 2023 01:44:28 +0700 Subject: [PATCH] sora: fix UHD --- Minioppai/build.gradle.kts | 25 ++ Minioppai/src/main/AndroidManifest.xml | 2 + .../src/main/kotlin/com/hexated/Minioppai.kt | 237 ++++++++++++++++++ .../kotlin/com/hexated/MinioppaiPlugin.kt | 13 + SoraStream/build.gradle.kts | 2 +- .../main/kotlin/com/hexated/SoraExtractor.kt | 95 ++++--- .../src/main/kotlin/com/hexated/SoraUtils.kt | 92 ++++--- 7 files changed, 404 insertions(+), 62 deletions(-) create mode 100644 Minioppai/build.gradle.kts create mode 100644 Minioppai/src/main/AndroidManifest.xml create mode 100644 Minioppai/src/main/kotlin/com/hexated/Minioppai.kt create mode 100644 Minioppai/src/main/kotlin/com/hexated/MinioppaiPlugin.kt diff --git a/Minioppai/build.gradle.kts b/Minioppai/build.gradle.kts new file mode 100644 index 00000000..41f322c8 --- /dev/null +++ b/Minioppai/build.gradle.kts @@ -0,0 +1,25 @@ +// use an integer for version numbers +version = 1 + + +cloudstream { + language = "id" + // All of these properties are optional, you can safely remove them + + // description = "Lorem Ipsum" + authors = listOf("Sora") + + /** + * Status int as the following: + * 0: Down + * 1: Ok + * 2: Slow + * 3: Beta only + * */ + status = 1 // will be 3 if unspecified + tvTypes = listOf( + "NSFW", + ) + + iconUrl = "https://www.google.com/s2/favicons?domain=minioppai.org&sz=%size%" +} \ No newline at end of file diff --git a/Minioppai/src/main/AndroidManifest.xml b/Minioppai/src/main/AndroidManifest.xml new file mode 100644 index 00000000..c98063f8 --- /dev/null +++ b/Minioppai/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/Minioppai/src/main/kotlin/com/hexated/Minioppai.kt b/Minioppai/src/main/kotlin/com/hexated/Minioppai.kt new file mode 100644 index 00000000..5f00d16b --- /dev/null +++ b/Minioppai/src/main/kotlin/com/hexated/Minioppai.kt @@ -0,0 +1,237 @@ +package com.hexated + +import com.fasterxml.jackson.annotation.JsonProperty +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.utils.* +import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson +import org.jsoup.Jsoup +import org.jsoup.nodes.Element +import java.net.URLDecoder + +class Minioppai : MainAPI() { + override var mainUrl = "https://minioppai.org" + override var name = "Minioppai" + override val hasMainPage = true + override var lang = "id" + override val hasDownloadSupport = true + override val hasQuickSearch = true + + override val supportedTypes = setOf( + TvType.NSFW, + ) + + companion object { + const val libPaistream = "https://lb.paistream.my.id" + const val paistream = "https://paistream.my.id" + + fun getType(t: String): TvType { + 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 { + return when (t) { + "Completed" -> ShowStatus.Completed + "Ongoing" -> ShowStatus.Ongoing + else -> ShowStatus.Completed + } + } + + } + + override val mainPage = mainPageOf( + "$mainUrl/watch" to "New Episode", + "$mainUrl/popular" to "Popular Hentai", + ) + + override suspend fun getMainPage( + page: Int, + request: MainPageRequest + ): HomePageResponse { + val document = app.get("${request.data}/page/$page").document + val home = document.select("div.latest a").mapNotNull { + it.toSearchResult() + } + return newHomePageResponse( + list = HomePageList( + name = request.name, + list = home, + isHorizontalImages = request.name == "New Episode" + ), + hasNext = true + ) + } + + private fun getProperAnimeLink(uri: String): String { + return if (uri.contains("-episode-")) { + uri.substringBefore("-episode-") + } else { + uri + } + } + + private fun Element.toSearchResult(): AnimeSearchResponse? { + val title = this.selectFirst("h2.entry-title")?.text()?.trim() ?: return null + val href = getProperAnimeLink(this.attr("href")) + val posterUrl = fixUrlNull(this.selectFirst("img")?.attr("src")) + val epNum = this.selectFirst("i.dot")?.text()?.filter { it.isDigit() }?.toIntOrNull() + return newAnimeSearchResponse(title, href, TvType.NSFW) { + this.posterUrl = posterUrl + addSub(epNum) + } + + } + + override suspend fun quickSearch(query: String): List? = search(query) + + override suspend fun search(query: String): List? { + return app.post( + "$mainUrl/wp-admin/admin-ajax.php", data = mapOf( + "action" to "ts_ac_do_search", + "ts_ac_query" to query, + ), headers = mapOf("X-Requested-With" to "XMLHttpRequest") + ).parsedSafe()?.post?.firstOrNull()?.all?.mapNotNull { item -> + newAnimeSearchResponse( + item.postTitle ?: "", + item.postLink ?: return@mapNotNull null, + TvType.NSFW + ) { + this.posterUrl = item.postImage + } + } + } + + override suspend fun load(url: String): LoadResponse? { + val document = app.get(url).document + + val title = document.selectFirst("h1.entry-title")?.text()?.trim() ?: return null + val poster = fixUrlNull(document.selectFirst("div.limage img")?.attr("src")) + val table = document.select("ul.data") + val tags = table.select("ul.data li:nth-child(1) a").map { it.text() } + val year = + document.selectFirst("ul.data time[itemprop=dateCreated]")?.text()?.substringBefore("-") + ?.toIntOrNull() + val status = getStatus(document.selectFirst("ul.data li:nth-child(2) span")?.text()?.trim()) + val description = document.select("div[itemprop=description] > p").text() + + val episodes = document.select("div.epsdlist ul li").mapNotNull { + val name = it.selectFirst("div.epl-num")?.text() + val link = fixUrlNull(it.selectFirst("a")?.attr("href")) ?: return@mapNotNull null + Episode(link, name = name) + }.reversed() + + return newAnimeLoadResponse(title, url, TvType.NSFW) { + engName = title + posterUrl = poster + this.year = year + addEpisodes(DubStatus.Subbed, episodes) + showStatus = status + plot = description + this.tags = tags + } + } + + override suspend fun loadLinks( + data: String, + isCasting: Boolean, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ): Boolean { + + val document = app.get(data).document + document.select("div.server ul.mirror li a").mapNotNull { + fixUrl( + Jsoup.parse(base64Decode(it.attr("data-em"))).select("iframe").attr("src") + ) to it.text() + }.apmap { (link, server) -> + if (link.startsWith(paistream)) { + invokeLocal(link, server, subtitleCallback, callback) + } else { + loadExtractor(fixUrl(decode(link.substringAfter("data="))), mainUrl, subtitleCallback, callback) + } + } + + return true + } + + private suspend fun invokeLocal( + url: String, + server: String, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ) { + val script = getAndUnpack(app.get(url).text) + val sources = script.substringAfter("sources:[").substringBefore("]").replace("'", "\"") + val subtitles = script.substringAfter("\"tracks\":[").substringBefore("]") + + tryParseJson>("[$sources]")?.map { + callback.invoke( + ExtractorLink( + server, + server, + fixLink(it.file ?: return@map, if(server == "Stream 1") libPaistream else paistream), + "$paistream/", + getQualityFromName(it.label) + ) + ) + } + + tryParseJson>("[$subtitles]")?.map { + subtitleCallback.invoke( + SubtitleFile( + it.label ?: "", + fixLink(it.file ?: return@map, paistream) + ) + ) + } + + } + + private fun decode(input: String): String = URLDecoder.decode(input, "utf-8") + + private fun fixLink(url: String, domain: String): String { + if (url.startsWith("http")) { + return url + } + if (url.isEmpty()) { + return "" + } + + val startsWithNoHttp = url.startsWith("//") + if (startsWithNoHttp) { + return "https:$url" + } else { + if (url.startsWith('/')) { + return domain + url + } + return "$domain/$url" + } + } + + data class Subtitles( + @JsonProperty("file") var file: String? = null, + @JsonProperty("label") var label: String? = null, + ) + + data class Sources( + @JsonProperty("label") var label: String? = null, + @JsonProperty("file") var file: String? = null, + ) + + data class SearchResponses( + @JsonProperty("post") var post: ArrayList = arrayListOf() + ) + + data class All( + @JsonProperty("ID") var ID: Int? = null, + @JsonProperty("post_image") var postImage: String? = null, + @JsonProperty("post_title") var postTitle: String? = null, + @JsonProperty("post_link") var postLink: String? = null, + ) + + data class Post( + @JsonProperty("all") var all: ArrayList = arrayListOf(), + ) + +} \ No newline at end of file diff --git a/Minioppai/src/main/kotlin/com/hexated/MinioppaiPlugin.kt b/Minioppai/src/main/kotlin/com/hexated/MinioppaiPlugin.kt new file mode 100644 index 00000000..3e99c433 --- /dev/null +++ b/Minioppai/src/main/kotlin/com/hexated/MinioppaiPlugin.kt @@ -0,0 +1,13 @@ +package com.hexated + +import com.lagradost.cloudstream3.plugins.CloudstreamPlugin +import com.lagradost.cloudstream3.plugins.Plugin +import android.content.Context + +@CloudstreamPlugin +class MinioppaiPlugin: Plugin() { + override fun load(context: Context) { + // All providers should be added in this manner. Please don't edit the providers list directly. + registerMainAPI(Minioppai()) + } +} \ No newline at end of file diff --git a/SoraStream/build.gradle.kts b/SoraStream/build.gradle.kts index 2027ffdb..bfb58419 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 = 136 +version = 137 android { defaultConfig { diff --git a/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt b/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt index 9116ba96..6b4c27aa 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt @@ -518,7 +518,8 @@ object SoraExtractor : SoraStream() { val cookiesDoc = mapOf( "G_ENABLED_IDPS" to "google", - "wordpress_logged_in_8bf9d5433ac88cc9a3a396d6b154cd01" to (filmxyCookies.wLog ?: return), + "wordpress_logged_in_8bf9d5433ac88cc9a3a396d6b154cd01" to (filmxyCookies.wLog + ?: return), "PHPSESSID" to (filmxyCookies.phpsessid ?: return) ) @@ -761,7 +762,13 @@ object SoraExtractor : SoraStream() { ?.attr("data-id") val servers = - app.get("$fmoviesAPI/ajax/server/list/${episodeId ?: return}?vrf=${comsumetEncodeVrf(episodeId)}") + app.get( + "$fmoviesAPI/ajax/server/list/${episodeId ?: return}?vrf=${ + comsumetEncodeVrf( + episodeId + ) + }" + ) .parsedSafe()?.result?.let { Jsoup.parse(it) } ?.select("ul li")?.map { it.attr("data-id") to it.attr("data-link-id") } @@ -769,8 +776,9 @@ object SoraExtractor : SoraStream() { it.first == "41" || it.first == "45" }?.apmap { (serverid, linkId) -> delay(2000) - val decryptServer = app.get("$fmoviesAPI/ajax/server/$linkId?vrf=${comsumetEncodeVrf(linkId)}") - .parsedSafe()?.result?.url?.let { comsumetDecodeVrf(it) } + val decryptServer = + app.get("$fmoviesAPI/ajax/server/$linkId?vrf=${comsumetEncodeVrf(linkId)}") + .parsedSafe()?.result?.url?.let { comsumetDecodeVrf(it) } if (serverid == "41") { invokeVizcloud(serverid, decryptServer ?: return@apmap, subtitleCallback, callback) } else { @@ -868,7 +876,12 @@ object SoraExtractor : SoraStream() { callback: (ExtractorLink) -> Unit ) { - val (aniId, malId) = convertTmdbToAnimeId(title, date, airedDate, if(season == null) TvType.AnimeMovie else TvType.Anime) + val (aniId, malId) = convertTmdbToAnimeId( + title, + date, + airedDate, + if (season == null) TvType.AnimeMovie else TvType.Anime + ) argamap( { @@ -881,7 +894,15 @@ object SoraExtractor : SoraStream() { invokeBiliBili(aniId, episode, subtitleCallback, callback) }, { - if (season != null) invokeCrunchyroll(aniId, malId, epsTitle, season, episode, subtitleCallback, callback) + if (season != null) invokeCrunchyroll( + aniId, + malId, + epsTitle, + season, + episode, + subtitleCallback, + callback + ) } ) } @@ -892,18 +913,27 @@ object SoraExtractor : SoraStream() { subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit ) { - val res = app.get("$biliBiliAPI/anime/episodes?id=${aniId ?: return}&source_id=bilibili", referer = kaguyaBaseUrl) + val res = app.get( + "$biliBiliAPI/anime/episodes?id=${aniId ?: return}&source_id=bilibili", + referer = kaguyaBaseUrl + ) .parsedSafe()?.episodes?.find { it.episodeNumber == episode } ?: return val sources = - app.get("$biliBiliAPI/source?episode_id=${res.sourceEpisodeId}&source_media_id=${res.sourceMediaId}&source_id=${res.sourceId}", referer = kaguyaBaseUrl) + app.get( + "$biliBiliAPI/source?episode_id=${res.sourceEpisodeId}&source_media_id=${res.sourceMediaId}&source_id=${res.sourceId}", + referer = kaguyaBaseUrl + ) .parsedSafe() sources?.sources?.apmap { source -> val quality = - app.get(source.file ?: return@apmap null, referer = kaguyaBaseUrl).document.selectFirst("Representation") + app.get( + source.file ?: return@apmap null, + referer = kaguyaBaseUrl + ).document.selectFirst("Representation") ?.attr("height") callback.invoke( ExtractorLink( @@ -946,21 +976,23 @@ object SoraExtractor : SoraStream() { }?.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}") - .parsedSafe()?.html?.let { Jsoup.parse(it) } - ?.select("div.item.server-item")?.map { - Triple( - it.text(), - it.attr("data-id"), - it.attr("data-type"), - ) - } + val servers = + app.get("$zoroAPI/ajax/v2/episode/servers?episodeId=${episodeId ?: return@apmap}") + .parsedSafe()?.html?.let { Jsoup.parse(it) } + ?.select("div.item.server-item")?.map { + Triple( + it.text(), + it.attr("data-id"), + it.attr("data-type"), + ) + } servers?.apmap servers@{ server -> - val iframe = app.get("$zoroAPI/ajax/v2/episode/sources?id=${server.second ?: return@servers}") - .parsedSafe()?.link ?: return@servers - val audio = if(server.third == "sub") "Raw" else "English Dub" - if(server.first == "Vidstreaming" || server.first == "Vidcloud") { + val iframe = + app.get("$zoroAPI/ajax/v2/episode/sources?id=${server.second ?: return@servers}") + .parsedSafe()?.link ?: return@servers + val audio = if (server.third == "sub") "Raw" else "English Dub" + if (server.first == "Vidstreaming" || server.first == "Vidcloud") { extractRabbitStream( "${server.first} [$audio]", iframe, @@ -971,7 +1003,7 @@ object SoraExtractor : SoraStream() { decryptKey = RabbitStream.getZoroKey() ) { it } } else { - loadExtractor(iframe,"$zoroAPI/", subtitleCallback, callback) + loadExtractor(iframe, "$zoroAPI/", subtitleCallback, callback) } } @@ -1503,14 +1535,18 @@ object SoraExtractor : SoraStream() { subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit ) { - val id = getCrunchyrollId("${aniId ?: return}") ?: getCrunchyrollIdFromMalSync("${malId ?: return}") ?: return + val id = getCrunchyrollId("${aniId ?: return}") + ?: getCrunchyrollIdFromMalSync("${malId ?: return}") ?: return val audioLocal = listOf( "ja-JP", "en-US", "zh-CN", ) val headers = getCrunchyrollToken() - val seasonIdData = app.get("$crunchyrollAPI/content/v2/cms/series/${id ?: return}/seasons", headers = headers) + val seasonIdData = app.get( + "$crunchyrollAPI/content/v2/cms/series/${id ?: return}/seasons", + headers = headers + ) .parsedSafe()?.data?.let { s -> if (s.size == 1) { s.firstOrNull() @@ -1897,7 +1933,7 @@ object SoraExtractor : SoraStream() { invokeSmashyDude(it.second, it.first, callback) } it.first.contains("/nflim") -> { - invokeSmashyNflim(it.second, it.first, callback) + invokeSmashyNflim(it.second, it.first, subtitleCallback, callback) } it.first.contains("/rip") -> { invokeSmashyRip(it.second, it.first, subtitleCallback, callback) @@ -2875,7 +2911,8 @@ object SoraExtractor : SoraStream() { episode: Int? = null, callback: (ExtractorLink) -> Unit ) { - val apiUrl = base64DecodeAPI("dWI=Y2w=cC4=bXU=ZWE=LWI=Ynk=YmE=bS4=ZWE=dHI=ZXM=aW4=LWM=NDA=MDg=NjE=YmQ=Y2I=MmU=Ly8=czo=dHA=aHQ=") + val apiUrl = + base64DecodeAPI("dWI=Y2w=cC4=bXU=ZWE=LWI=Ynk=YmE=bS4=ZWE=dHI=ZXM=aW4=LWM=NDA=MDg=NjE=YmQ=Y2I=MmU=Ly8=czo=dHA=aHQ=") val url = if (season == null) { "$apiUrl/stream/movie/$imdbId.json" } else { @@ -2904,7 +2941,7 @@ object SoraExtractor : SoraStream() { ) { val referer = "https://2now.tv/" val url = "$nowTvAPI/$tmdbId.mp4" - if(!app.get(url, referer = referer).isSuccessful) return + if (!app.get(url, referer = referer).isSuccessful) return callback.invoke( ExtractorLink( "NowTv", @@ -3362,5 +3399,5 @@ data class ZoroResponses( ) data class MalSyncRes( - @JsonProperty("Sites") val Sites: Map>>? = null, + @JsonProperty("Sites") val Sites: Map>>? = null, ) \ 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 609e0953..d5793c8a 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraUtils.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraUtils.kt @@ -435,7 +435,8 @@ suspend fun invokeSmashyFfix( callback: (ExtractorLink) -> Unit, ) { val script = - app.get(url, referer = ref).document.selectFirst("script:containsData(player =)")?.data() ?: return + app.get(url, referer = ref).document.selectFirst("script:containsData(player =)")?.data() + ?: return val source = Regex("file:\\s['\"](\\S+?)['|\"]").find(script)?.groupValues?.get( @@ -516,30 +517,38 @@ suspend fun invokeSmashyDude( suspend fun invokeSmashyNflim( name: String, url: String, + subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit, ) { val script = app.get(url).document.selectFirst("script:containsData(player =)")?.data() ?: return - val source = - Regex("file:\\s*\"([^\"]+)").find(script)?.groupValues?.get( - 1 - ) ?: return + val sources = Regex("file:\\s*\"([^\"]+)").find(script)?.groupValues?.get(1) ?: return + val subtitles = Regex("subtitle:\\s*\"([^\"]+)").find(script)?.groupValues?.get(1) ?: return - source.split(",").map { links -> + sources.split(",").map { links -> val quality = Regex("\\[(\\d+)]").find(links)?.groupValues?.getOrNull(1)?.trim() val trimmedLink = links.removePrefix("[$quality]").trim() callback.invoke( ExtractorLink( "Smashy [$name]", "Smashy [$name]", - trimmedLink.substringAfter("url=").substringBefore("&cookie=").trim(), + trimmedLink, "", quality?.toIntOrNull() ?: return@map, isM3u8 = true, - headers = mapOf( - "Cookie" to trimmedLink.substringAfter("&cookie=").trim() - ) + ) + ) + } + + subtitles.split(",").map { sub -> + val lang = Regex("\\[(.*?)]").find(sub)?.groupValues?.getOrNull(1)?.trim() + val trimmedSubLink = sub.removePrefix("[$lang]").trim() + + subtitleCallback.invoke( + SubtitleFile( + lang ?: return@map, + trimmedSubLink ) ) } @@ -561,7 +570,7 @@ suspend fun invokeSmashyRip( source?.split(",")?.map { links -> val quality = Regex("\\[(\\d+)]").find(links)?.groupValues?.getOrNull(1)?.trim() val link = links.removePrefix("[$quality]").substringAfter("dev/").trim() - if(link.isEmpty()) return@map + if (link.isEmpty()) return@map callback.invoke( ExtractorLink( "Smashy [$name]", @@ -743,29 +752,38 @@ suspend fun bypassHrefli(url: String): String? { } suspend fun bypassTechmny(url: String): String? { + val techRes = app.get(url).document val postUrl = url.substringBefore("?id=").substringAfter("/?") - var res = app.post( - postUrl, data = mapOf( - "_wp_http_c" to url.substringAfter("?id=") + val (goUrl, goHeader) = if (techRes.selectFirst("form#landing input[name=_wp_http_c]") != null) { + var res = app.post( + postUrl, data = mapOf( + "_wp_http_c" to url.substringAfter("?id=") + ) ) - ) - val (longC, catC, _) = getTechmnyCookies(res.text) - var headers = mapOf("Cookie" to "$longC; $catC") - var formLink = res.document.selectFirst("center a")?.attr("href") - - res = app.get(formLink ?: return null, headers = headers) - val (longC2, _, postC) = getTechmnyCookies(res.text) - headers = mapOf("Cookie" to "$catC; $longC2; $postC") - formLink = res.document.selectFirst("center a")?.attr("href") - - res = app.get(formLink ?: return null, headers = headers) - val goToken = res.text.substringAfter("?go=").substringBefore("\"") - val tokenUrl = "$postUrl?go=$goToken" - val newLongC = "$goToken=" + longC2.substringAfter("=") - headers = mapOf("Cookie" to "$catC; rdst_post=; $newLongC") + val (longC, catC, _) = getTechmnyCookies(res.text) + var headers = mapOf("Cookie" to "$longC; $catC") + var formLink = res.document.selectFirst("center a")?.attr("href") + res = app.get(formLink ?: return null, headers = headers) + val (longC2, _, postC) = getTechmnyCookies(res.text) + headers = mapOf("Cookie" to "$catC; $longC2; $postC") + formLink = res.document.selectFirst("center a")?.attr("href") + res = app.get(formLink ?: return null, headers = headers) + val goToken = res.text.substringAfter("?go=").substringBefore("\"") + val tokenUrl = "$postUrl?go=$goToken" + val newLongC = "$goToken=" + longC2.substringAfter("=") + headers = mapOf("Cookie" to "$catC; rdst_post=; $newLongC") + Pair(tokenUrl, headers) + } else { + val secondPage = techRes.getNextTechPage().document + val thirdPage = secondPage.getNextTechPage().text + val goToken = thirdPage.substringAfter("?go=").substringBefore("\"") + val tokenUrl = "$postUrl?go=$goToken" + val headers = mapOf("Cookie" to "$goToken=${secondPage.select("form#landing input[name=_wp_http2]").attr("value")}") + Pair(tokenUrl, headers) + } val driveUrl = - app.get(tokenUrl, headers = headers).document.selectFirst("meta[http-equiv=refresh]") + app.get(goUrl, headers = goHeader).document.selectFirst("meta[http-equiv=refresh]") ?.attr("content")?.substringAfter("url=") val path = app.get(driveUrl ?: return null).text.substringAfter("replace(\"") .substringBefore("\")") @@ -773,6 +791,15 @@ suspend fun bypassTechmny(url: String): String? { return fixUrl(path, getBaseUrl(driveUrl)) } +private suspend fun Document.getNextTechPage(): NiceResponse { + return app.post( + this.select("form").attr("action"), + data = this.select("form input").mapNotNull { + it.attr("name") to it.attr("value") + }.toMap().toMutableMap() + ) +} + suspend fun bypassDriveleech(url: String): String? { val path = app.get(url).text.substringAfter("replace(\"") .substringBefore("\")") @@ -990,7 +1017,8 @@ suspend fun getCrunchyrollIdFromMalSync(aniId: String?): String? { val vrv = res?.get("Vrv")?.map { it.value }?.firstOrNull()?.get("url") val crunchyroll = res?.get("Vrv")?.map { it.value }?.firstOrNull()?.get("url") val regex = Regex("series/(\\w+)/?") - return regex.find("$vrv")?.groupValues?.getOrNull(1) ?: regex.find("$crunchyroll")?.groupValues?.getOrNull(1) + return regex.find("$vrv")?.groupValues?.getOrNull(1) + ?: regex.find("$crunchyroll")?.groupValues?.getOrNull(1) } suspend fun extractPutlockerSources(url: String?): NiceResponse? { @@ -1118,7 +1146,7 @@ fun getSeason(month: Int?): String? { "Winter", "Winter", "Spring", "Spring", "Spring", "Summer", "Summer", "Summer", "Fall", "Fall", "Fall", "Winter" ) - if(month == null) return null + if (month == null) return null return seasons[month - 1] }