From 864128bb4763a2e4b94e137c5131c77deae95aae Mon Sep 17 00:00:00 2001 From: Samfun75 <38332931+Samfun75@users.noreply.github.com> Date: Sat, 27 May 2023 23:20:09 +0300 Subject: [PATCH] UHDMovies: update video extractor (#1649) --- src/en/uhdmovies/build.gradle | 2 +- .../animeextension/en/uhdmovies/UHDMovies.kt | 83 +++++++++++++------ 2 files changed, 58 insertions(+), 27 deletions(-) diff --git a/src/en/uhdmovies/build.gradle b/src/en/uhdmovies/build.gradle index f58447120..a610a8c45 100644 --- a/src/en/uhdmovies/build.gradle +++ b/src/en/uhdmovies/build.gradle @@ -6,7 +6,7 @@ ext { extName = 'UHD Movies' pkgNameSuffix = 'en.uhdmovies' extClass = '.UHDMovies' - extVersionCode = 14 + extVersionCode = 15 libVersion = '13' } diff --git a/src/en/uhdmovies/src/eu/kanade/tachiyomi/animeextension/en/uhdmovies/UHDMovies.kt b/src/en/uhdmovies/src/eu/kanade/tachiyomi/animeextension/en/uhdmovies/UHDMovies.kt index 21911d31d..2a9fd11b4 100644 --- a/src/en/uhdmovies/src/eu/kanade/tachiyomi/animeextension/en/uhdmovies/UHDMovies.kt +++ b/src/en/uhdmovies/src/eu/kanade/tachiyomi/animeextension/en/uhdmovies/UHDMovies.kt @@ -21,13 +21,13 @@ import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.Serializable -import kotlinx.serialization.decodeFromString import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json import okhttp3.FormBody import okhttp3.Headers import okhttp3.OkHttpClient import okhttp3.Request +import okhttp3.Response import org.jsoup.Jsoup import org.jsoup.nodes.Document import org.jsoup.nodes.Element @@ -121,8 +121,8 @@ class UHDMovies : ConfigurableAnimeSource, ParsedAnimeHttpSource() { override fun animeDetailsParse(document: Document): SAnime { return SAnime.create().apply { initialized = true - title = document.selectFirst(".entry-title")!!.text() - .replace("Download", "", true).trim() + title = document.selectFirst(".entry-title")?.text() + ?.replace("Download", "", true)?.trim() ?: "Movie" status = SAnime.COMPLETED description = document.selectFirst("pre:contains(plot)")?.text() } @@ -135,7 +135,7 @@ class UHDMovies : ConfigurableAnimeSource, ParsedAnimeHttpSource() { val episodeList = mutableListOf() val episodeElements = resp.select("p:has(a[href*=?id=],a[href*=r?key=]):has(a[class*=maxbutton])[style*=center]") val qualityRegex = "\\d{3,4}p".toRegex(RegexOption.IGNORE_CASE) - val firstText = episodeElements.first()!!.text() + val firstText = episodeElements.first()?.text() ?: "" if (firstText.contains("Episode", true) || firstText.contains("Zip", true) || firstText.contains("Pack", true) @@ -151,9 +151,9 @@ class UHDMovies : ConfigurableAnimeSource, ParsedAnimeHttpSource() { part = partRegex.find(prevP.text())?.groups?.get(1)?.value ?: "" } ?: let { val prevPre = row.previousElementSiblings().prev("pre,div.mks_separator") - val preResult = seasonRegex.find(prevPre.first()!!.text()) + val preResult = seasonRegex.find(prevPre.first()?.text() ?: "") preResult?.groups?.get(1)?.value?.also { - part = partRegex.find(prevPre.first()!!.text())?.groups?.get(1)?.value ?: "" + part = partRegex.find(prevPre.first()?.text() ?: "")?.groups?.get(1)?.value ?: "" } ?: let { val title = resp.select("h1.entry-title") val titleResult = "[ .\\[(]?S(?:eason)?[ .]?(\\d{1,2})[ .\\])]?" @@ -182,7 +182,7 @@ class UHDMovies : ConfigurableAnimeSource, ParsedAnimeHttpSource() { ?.trim()?.toIntOrNull() ?: (index + 1) Triple( season + "_$episode" + "_$part", - linkElement.attr("href") ?: return@mapIndexed null, + linkElement?.attr("href") ?: return@mapIndexed null, quality, ) }.filterNotNull() @@ -292,28 +292,46 @@ class UHDMovies : ConfigurableAnimeSource, ParsedAnimeHttpSource() { // ============================= Utilities ============================== private fun extractVideo(epUrl: EpUrl): Pair, String> { + val noRedirectClient = client.newBuilder().followRedirects(false).build() val mediaResponse = if (epUrl.url.contains("?id=")) { val postLink = epUrl.url.substringBefore("?id=").substringAfter("/?") - val formData = FormBody.Builder().add("_wp_http_c", epUrl.url.substringAfter("?id=")).build() - val response = client.newCall(POST(postLink, body = formData)).execute().body.string() - val (longC, catC, _) = getCookiesDetail(response) - val cookieHeader = Headers.headersOf("Cookie", "$longC; $catC") - val parsedSoup = Jsoup.parse(response) - val link = parsedSoup.selectFirst("center > a")!!.attr("href") + val initailUrl = epUrl.url.substringAfter("/?http").let { + if (it.startsWith("http")) { + it + } else { + "http$it" + } + } + val initialResp = noRedirectClient.newCall(GET(initailUrl)).execute().asJsoup() + val (tokenUrl, tokenCookie) = if (initialResp.selectFirst("form#landing input[name=_wp_http_c]") != null) { + val formData = FormBody.Builder().add("_wp_http_c", epUrl.url.substringAfter("?id=")).build() + val response = client.newCall(POST(postLink, body = formData)).execute().body.string() + val (longC, catC, _) = getCookiesDetail(response) + val cookieHeader = Headers.headersOf("Cookie", "$longC; $catC") + val parsedSoup = Jsoup.parse(response) + val link = parsedSoup.selectFirst("center > a")!!.attr("href") - val response2 = client.newCall(GET(link, cookieHeader)).execute().body.string() - val (longC2, _, postC) = getCookiesDetail(response2) - val cookieHeader2 = Headers.headersOf("Cookie", "$catC; $longC2; $postC") - val parsedSoup2 = Jsoup.parse(response2) - val link2 = parsedSoup2.selectFirst("center > a")!!.attr("href") + val response2 = client.newCall(GET(link, cookieHeader)).execute().body.string() + val (longC2, _, postC) = getCookiesDetail(response2) + val cookieHeader2 = Headers.headersOf("Cookie", "$catC; $longC2; $postC") + val parsedSoup2 = Jsoup.parse(response2) + val link2 = parsedSoup2.selectFirst("center > a")!!.attr("href") + val tokenResp = client.newCall(GET(link2, cookieHeader2)).execute().body.string() + val goToken = tokenResp.substringAfter("?go=").substringBefore("\"") + val tokenUrl = "$postLink?go=$goToken" + val newLongC = "$goToken=" + longC2.substringAfter("=") + val tokenCookie = Headers.headersOf("Cookie", "$catC; rdst_post=; $newLongC") + Pair(tokenUrl, tokenCookie) + } else { + val secondResp = initialResp.getNextResp().asJsoup() + val thirdResp = secondResp.getNextResp().body.string() + val goToken = thirdResp.substringAfter("?go=").substringBefore("\"") + val tokenUrl = "$postLink?go=$goToken" + val cookie = secondResp.selectFirst("form#landing input[name=_wp_http2]")?.attr("value") + val tokenCookie = Headers.headersOf("Cookie", "$goToken=$cookie") + Pair(tokenUrl, tokenCookie) + } - val tokenResp = client.newCall(GET(link2, cookieHeader2)).execute().body.string() - val goToken = tokenResp.substringAfter("?go=").substringBefore("\"") - val tokenUrl = "$postLink?go=$goToken" - val newLongC = "$goToken=" + longC2.substringAfter("=") - val tokenCookie = Headers.headersOf("Cookie", "$catC; rdst_post=; $newLongC") - - val noRedirectClient = client.newBuilder().followRedirects(false).build() val tokenResponse = noRedirectClient.newCall(GET(tokenUrl, tokenCookie)).execute().asJsoup() val redirectUrl = tokenResponse.select("meta[http-equiv=refresh]").attr("content") .substringAfter("url=").substringBefore("\"") @@ -335,6 +353,19 @@ class UHDMovies : ConfigurableAnimeSource, ParsedAnimeHttpSource() { return Pair(videoList, mediaUrl) } + private fun Document.getNextResp(): Response { + val form = this.selectFirst("form#landing") ?: throw Exception("Failed to find form") + val postLink = form.attr("action") + val formData = FormBody.Builder().let { fd -> + form.select("input").map { + fd.add(it.attr("name"), it.attr("value")) + } + fd.build() + } + + return client.newCall(POST(postLink, body = formData)).execute() + } + private fun getCookiesDetail(page: String): Triple { val cat = "rdst_cat" val post = "rdst_post" @@ -395,7 +426,7 @@ class UHDMovies : ConfigurableAnimeSource, ParsedAnimeHttpSource() { val size = sizeMatch?.groups?.get(1)?.value?.let { " - $it" } ?: "" val gdResponse = client.newCall(GET(gdLink)).execute().asJsoup() val link = gdResponse.select("form#download-form") - return if (link.isNullOrEmpty()) { + return if (link.isEmpty()) { listOf() } else { val realLink = link.attr("action")