Change video extraction (#1534)

This commit is contained in:
Secozzi
2023-04-22 12:55:16 +02:00
committed by GitHub
parent 477ceee1c7
commit 6bc4f3dd79
2 changed files with 58 additions and 54 deletions

View File

@ -1,11 +1,12 @@
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
apply plugin: 'kotlinx-serialization'
ext { ext {
extName = 'BestDubbedAnime' extName = 'BestDubbedAnime'
pkgNameSuffix = 'en.bestdubbedanime' pkgNameSuffix = 'en.bestdubbedanime'
extClass = '.BestDubbedAnime' extClass = '.BestDubbedAnime'
extVersionCode = 2 extVersionCode = 3
libVersion = '13' libVersion = '13'
} }

View File

@ -5,7 +5,6 @@ import android.content.SharedPreferences
import android.util.Base64 import android.util.Base64
import androidx.preference.ListPreference import androidx.preference.ListPreference
import androidx.preference.PreferenceScreen import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.animeextension.en.bestdubbedanime.extractors.DailyMotionExtractor
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
import eu.kanade.tachiyomi.animesource.model.AnimeFilter import eu.kanade.tachiyomi.animesource.model.AnimeFilter
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
@ -16,6 +15,11 @@ import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.animesource.online.ParsedAnimeHttpSource import eu.kanade.tachiyomi.animesource.online.ParsedAnimeHttpSource
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.runBlocking
import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.JsonObject
@ -32,6 +36,7 @@ import org.jsoup.nodes.Document
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import java.lang.Exception import java.lang.Exception
class BestDubbedAnime : ConfigurableAnimeSource, ParsedAnimeHttpSource() { class BestDubbedAnime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
@ -46,6 +51,8 @@ class BestDubbedAnime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override val client: OkHttpClient = network.cloudflareClient override val client: OkHttpClient = network.cloudflareClient
private val json: Json by injectLazy()
private val preferences: SharedPreferences by lazy { private val preferences: SharedPreferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000) Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
} }
@ -81,7 +88,6 @@ class BestDubbedAnime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override fun popularAnimeNextPageSelector(): String = throw Exception("Not used") override fun popularAnimeNextPageSelector(): String = throw Exception("Not used")
// Episodes // Episodes
override fun episodeListSelector() = throw Exception("Not used") override fun episodeListSelector() = throw Exception("Not used")
override fun episodeListParse(response: Response): List<SEpisode> { override fun episodeListParse(response: Response): List<SEpisode> {
@ -161,64 +167,55 @@ class BestDubbedAnime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
return String(Base64.decode(inputStr.replace("\\x", "").decodeHex(), Base64.DEFAULT)) return String(Base64.decode(inputStr.replace("\\x", "").decodeHex(), Base64.DEFAULT))
} }
@Serializable
data class ServerResponse(
val result: ResultObject,
) {
@Serializable
data class ResultObject(
val anime: List<AnimeObject>,
) {
@Serializable
data class AnimeObject(
val serversHTML: String,
)
}
}
override fun videoListParse(response: Response): List<Video> { override fun videoListParse(response: Response): List<Video> {
val videoList = mutableListOf<Video>() val videoList = mutableListOf<Video>()
var slug = response.request.url.toString().split(".com/")[1] val slug = response.request.url.encodedPath.substringAfter("/")
if (slug.startsWith("movies/")) { val serverHeaders = headers.newBuilder()
slug = slug.split("movies/")[1] .add("Accept", "application/json, text/javascript, */*; q=0.01")
} .add("Host", baseUrl.toHttpUrl().host)
.add("Referer", response.request.url.toString())
.add("X-Requested-With", "XMLHttpRequest")
.build()
val jsString = client.newCall( val serversResponse = client.newCall(
GET("$baseUrl/xz/v3/js/index_beta.js?999995b"), GET("$baseUrl/xz/v3/jsonEpi.php?slug=$slug&_=${System.currentTimeMillis() / 1000}", headers = serverHeaders),
).execute().body.string() ).execute().body.string()
val parsed = json.decodeFromString<ServerResponse>(serversResponse)
val apiPath = if (response.request.url.encodedPath.startsWith("/movies/")) { Jsoup.parse(parsed.result.anime.first().serversHTML).select("div.serversks").parallelMap { player ->
"/movies/jsonMovie.php?slug=" val playerHeaders = headers.newBuilder()
} else { .add("Accept", "*/*")
decodeAtob(jsString.substringAfter("var Epinfri = window.atob('").substringBefore("');")) .add("Host", baseUrl.toHttpUrl().host)
} .add("Referer", response.request.url.toString())
val playerUrl = decodeAtob(jsString.substringAfter("var gkrrxx = '").substringBefore("';")) .add("X-Requested-With", "XMLHttpRequest")
.build()
val apiResp = client.newCall( runCatching {
GET(baseUrl + apiPath + slug + "&_=${System.currentTimeMillis() / 1000}"), val playerResponse = client.newCall(
).execute() GET("$baseUrl/xz/api/playeri.php?url=${player.attr("hl")}&_=${System.currentTimeMillis() / 1000}", headers = playerHeaders),
).execute().asJsoup()
val apiJson = apiResp.body.let { Json.decodeFromString<JsonObject>(it.string()) } playerResponse.select("source").forEach { source ->
val url = source.attr("src")
val serversHtml = apiJson!!["result"]!! if (url.isNotBlank()) {
.jsonObject["anime"]!! videoList.add(
.jsonArray[0] Video(url, "${source.attr("label")}p ${player.text()}", url),
.jsonObject["serversHTML"]!! )
.jsonPrimitive.content
val serversSoup = Jsoup.parse(serversHtml)
for (server in serversSoup.select("body > div")) {
if (server.attr("isembedurl") == "true") {
val iframeUrl = String(Base64.decode(server.attr("hl"), Base64.DEFAULT))
when {
iframeUrl.contains("dailymotion.com") -> {
val extractor = DailyMotionExtractor(client)
for (video in extractor.videoFromUrl(iframeUrl)) {
videoList.add(video)
}
} }
} }
} else {
val sourceElement = client.newCall(
GET("https:" + playerUrl + server.attr("hl") + "&_=${System.currentTimeMillis() / 1000}"),
).execute().asJsoup().selectFirst("source")!!
val videoUrl = sourceElement.attr("src").replace("^//".toRegex(), "https://")
videoList.add(
Video(
videoUrl,
"1080p (${server.select("small").text()})",
videoUrl,
),
)
} }
} }
@ -227,6 +224,12 @@ class BestDubbedAnime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override fun videoListSelector() = throw Exception("Not used") override fun videoListSelector() = throw Exception("Not used")
// From Dopebox
private fun <A, B> Iterable<A>.parallelMap(f: suspend (A) -> B): List<B> =
runBlocking {
map { async(Dispatchers.Default) { f(it) } }.awaitAll()
}
override fun List<Video>.sort(): List<Video> { override fun List<Video>.sort(): List<Video> {
val quality = preferences.getString("preferred_quality", null) val quality = preferences.getString("preferred_quality", null)
if (quality != null) { if (quality != null) {