UHDMovies: update video extractor (#1649)

This commit is contained in:
Samfun75
2023-05-27 23:20:09 +03:00
committed by GitHub
parent 0586be3861
commit 864128bb47
2 changed files with 58 additions and 27 deletions

View File

@ -6,7 +6,7 @@ ext {
extName = 'UHD Movies' extName = 'UHD Movies'
pkgNameSuffix = 'en.uhdmovies' pkgNameSuffix = 'en.uhdmovies'
extClass = '.UHDMovies' extClass = '.UHDMovies'
extVersionCode = 14 extVersionCode = 15
libVersion = '13' libVersion = '13'
} }

View File

@ -21,13 +21,13 @@ import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import okhttp3.FormBody import okhttp3.FormBody
import okhttp3.Headers import okhttp3.Headers
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import okhttp3.Response
import org.jsoup.Jsoup import org.jsoup.Jsoup
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
@ -121,8 +121,8 @@ class UHDMovies : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override fun animeDetailsParse(document: Document): SAnime { override fun animeDetailsParse(document: Document): SAnime {
return SAnime.create().apply { return SAnime.create().apply {
initialized = true initialized = true
title = document.selectFirst(".entry-title")!!.text() title = document.selectFirst(".entry-title")?.text()
.replace("Download", "", true).trim() ?.replace("Download", "", true)?.trim() ?: "Movie"
status = SAnime.COMPLETED status = SAnime.COMPLETED
description = document.selectFirst("pre:contains(plot)")?.text() description = document.selectFirst("pre:contains(plot)")?.text()
} }
@ -135,7 +135,7 @@ class UHDMovies : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
val episodeList = mutableListOf<SEpisode>() val episodeList = mutableListOf<SEpisode>()
val episodeElements = resp.select("p:has(a[href*=?id=],a[href*=r?key=]):has(a[class*=maxbutton])[style*=center]") 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 qualityRegex = "\\d{3,4}p".toRegex(RegexOption.IGNORE_CASE)
val firstText = episodeElements.first()!!.text() val firstText = episodeElements.first()?.text() ?: ""
if (firstText.contains("Episode", true) || if (firstText.contains("Episode", true) ||
firstText.contains("Zip", true) || firstText.contains("Zip", true) ||
firstText.contains("Pack", true) firstText.contains("Pack", true)
@ -151,9 +151,9 @@ class UHDMovies : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
part = partRegex.find(prevP.text())?.groups?.get(1)?.value ?: "" part = partRegex.find(prevP.text())?.groups?.get(1)?.value ?: ""
} ?: let { } ?: let {
val prevPre = row.previousElementSiblings().prev("pre,div.mks_separator") 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 { 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 { } ?: let {
val title = resp.select("h1.entry-title") val title = resp.select("h1.entry-title")
val titleResult = "[ .\\[(]?S(?:eason)?[ .]?(\\d{1,2})[ .\\])]?" val titleResult = "[ .\\[(]?S(?:eason)?[ .]?(\\d{1,2})[ .\\])]?"
@ -182,7 +182,7 @@ class UHDMovies : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
?.trim()?.toIntOrNull() ?: (index + 1) ?.trim()?.toIntOrNull() ?: (index + 1)
Triple( Triple(
season + "_$episode" + "_$part", season + "_$episode" + "_$part",
linkElement.attr("href") ?: return@mapIndexed null, linkElement?.attr("href") ?: return@mapIndexed null,
quality, quality,
) )
}.filterNotNull() }.filterNotNull()
@ -292,8 +292,18 @@ class UHDMovies : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
// ============================= Utilities ============================== // ============================= Utilities ==============================
private fun extractVideo(epUrl: EpUrl): Pair<List<Video>, String> { private fun extractVideo(epUrl: EpUrl): Pair<List<Video>, String> {
val noRedirectClient = client.newBuilder().followRedirects(false).build()
val mediaResponse = if (epUrl.url.contains("?id=")) { val mediaResponse = if (epUrl.url.contains("?id=")) {
val postLink = epUrl.url.substringBefore("?id=").substringAfter("/?") val postLink = epUrl.url.substringBefore("?id=").substringAfter("/?")
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 formData = FormBody.Builder().add("_wp_http_c", epUrl.url.substringAfter("?id=")).build()
val response = client.newCall(POST(postLink, body = formData)).execute().body.string() val response = client.newCall(POST(postLink, body = formData)).execute().body.string()
val (longC, catC, _) = getCookiesDetail(response) val (longC, catC, _) = getCookiesDetail(response)
@ -306,14 +316,22 @@ class UHDMovies : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
val cookieHeader2 = Headers.headersOf("Cookie", "$catC; $longC2; $postC") val cookieHeader2 = Headers.headersOf("Cookie", "$catC; $longC2; $postC")
val parsedSoup2 = Jsoup.parse(response2) val parsedSoup2 = Jsoup.parse(response2)
val link2 = parsedSoup2.selectFirst("center > a")!!.attr("href") val link2 = parsedSoup2.selectFirst("center > a")!!.attr("href")
val tokenResp = client.newCall(GET(link2, cookieHeader2)).execute().body.string() val tokenResp = client.newCall(GET(link2, cookieHeader2)).execute().body.string()
val goToken = tokenResp.substringAfter("?go=").substringBefore("\"") val goToken = tokenResp.substringAfter("?go=").substringBefore("\"")
val tokenUrl = "$postLink?go=$goToken" val tokenUrl = "$postLink?go=$goToken"
val newLongC = "$goToken=" + longC2.substringAfter("=") val newLongC = "$goToken=" + longC2.substringAfter("=")
val tokenCookie = Headers.headersOf("Cookie", "$catC; rdst_post=; $newLongC") 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 noRedirectClient = client.newBuilder().followRedirects(false).build()
val tokenResponse = noRedirectClient.newCall(GET(tokenUrl, tokenCookie)).execute().asJsoup() val tokenResponse = noRedirectClient.newCall(GET(tokenUrl, tokenCookie)).execute().asJsoup()
val redirectUrl = tokenResponse.select("meta[http-equiv=refresh]").attr("content") val redirectUrl = tokenResponse.select("meta[http-equiv=refresh]").attr("content")
.substringAfter("url=").substringBefore("\"") .substringAfter("url=").substringBefore("\"")
@ -335,6 +353,19 @@ class UHDMovies : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
return Pair(videoList, mediaUrl) 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<String, String, String> { private fun getCookiesDetail(page: String): Triple<String, String, String> {
val cat = "rdst_cat" val cat = "rdst_cat"
val post = "rdst_post" val post = "rdst_post"
@ -395,7 +426,7 @@ class UHDMovies : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
val size = sizeMatch?.groups?.get(1)?.value?.let { " - $it" } ?: "" val size = sizeMatch?.groups?.get(1)?.value?.let { " - $it" } ?: ""
val gdResponse = client.newCall(GET(gdLink)).execute().asJsoup() val gdResponse = client.newCall(GET(gdLink)).execute().asJsoup()
val link = gdResponse.select("form#download-form") val link = gdResponse.select("form#download-form")
return if (link.isNullOrEmpty()) { return if (link.isEmpty()) {
listOf() listOf()
} else { } else {
val realLink = link.attr("action") val realLink = link.attr("action")