fix(pt/animesdigital): Fix video extractor (#2892)

This commit is contained in:
Claudemirovsky 2024-02-08 10:46:54 -03:00 committed by GitHub
parent 566b9e43f0
commit a77dcad9cf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 85 additions and 57 deletions

View File

@ -8,4 +8,4 @@ apply from: "$rootDir/common.gradle"
dependencies {
implementation(project(":lib:unpacker"))
}
}

View File

@ -3,6 +3,8 @@ package eu.kanade.tachiyomi.animeextension.pt.animesdigital
import android.app.Application
import androidx.preference.ListPreference
import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.animeextension.pt.animesdigital.extractors.ProtectorExtractor
import eu.kanade.tachiyomi.animeextension.pt.animesdigital.extractors.ScriptExtractor
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
import eu.kanade.tachiyomi.animesource.model.AnimesPage
@ -10,14 +12,12 @@ import eu.kanade.tachiyomi.animesource.model.SAnime
import eu.kanade.tachiyomi.animesource.model.SEpisode
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.animesource.online.ParsedAnimeHttpSource
import eu.kanade.tachiyomi.lib.unpacker.Unpacker
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.network.awaitSuccess
import eu.kanade.tachiyomi.util.asJsoup
import eu.kanade.tachiyomi.util.parseAs
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import okhttp3.FormBody
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Request
@ -27,7 +27,6 @@ import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
class AnimesDigital : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
@ -41,8 +40,6 @@ class AnimesDigital : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override fun headersBuilder() = super.headersBuilder().add("Referer", baseUrl)
private val json: Json by injectLazy()
private val preferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
}
@ -126,7 +123,7 @@ class AnimesDigital : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override fun searchAnimeParse(response: Response): AnimesPage {
return runCatching {
val data = response.parseAs<SearchResponseDto>()
val animes = data.results.map(Jsoup::parse)
val animes = data.results.map(Jsoup::parseBodyFragment)
.mapNotNull { it.selectFirst(searchAnimeSelector()) }
.map(::searchAnimeFromElement)
val hasNext = data.total_page > data.page
@ -156,15 +153,15 @@ class AnimesDigital : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
else -> SAnime.UNKNOWN
}
val infos = doc.selectFirst("div.crw > div.dados")!!
with(doc.selectFirst("div.crw > div.dados")!!) {
artist = getInfo("Estúdio")
author = getInfo("Autor") ?: getInfo("Diretor")
artist = infos.getInfo("Estúdio")
author = infos.getInfo("Autor") ?: infos.getInfo("Diretor")
title = selectFirst("h1")!!.text()
genre = select("div.genre a").eachText().joinToString()
title = infos.selectFirst("h1")!!.text()
genre = infos.select("div.genre a").eachText().joinToString()
description = infos.selectFirst("div.sinopse")?.text()
description = selectFirst("div.sinopse")?.text()
}
}
// ============================== Episodes ==============================
@ -195,9 +192,9 @@ class AnimesDigital : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
episode_number = epname.substringAfterLast(" ").toFloatOrNull() ?: 1F
name = buildString {
append(epname)
element.selectFirst("div.sub_title")?.text()?.let {
element.selectFirst("div.sub_title")?.text()?.also {
if (!it.contains("Ainda não tem um titulo oficial")) {
append(" - $it")
append(" - ", it)
}
}
}
@ -207,7 +204,9 @@ class AnimesDigital : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override fun videoListParse(response: Response): List<Video> {
val player = response.asJsoup().selectFirst("div#player")!!
return player.select("div.tab-video").flatMap { div ->
div.select(videoListSelector()).flatMap { element ->
val noComment = div.outerHtml().replace("<!--", "").replace("-->", "")
val newDoc = Jsoup.parseBodyFragment(noComment)
newDoc.select(videoListSelector()).ifEmpty { newDoc.select("a") }.flatMap { element ->
runCatching {
videosFromElement(element)
}.onFailure { it.printStackTrace() }.getOrElse { emptyList() }
@ -215,52 +214,28 @@ class AnimesDigital : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
}
}
private val protectorExtractor by lazy { ProtectorExtractor(client) }
private fun videosFromElement(element: Element): List<Video> {
return when (element.tagName()) {
"iframe" -> {
val url = element.attr("data-lazy-src").ifEmpty { element.attr("src") }
.let {
when {
it.startsWith("/") -> baseUrl + it
else -> it
}
}
val url = element.absUrl("data-lazy-src").ifEmpty { element.absUrl("src") }
client.newCall(GET(url, headers)).execute()
.asJsoup()
.select(videoListSelector())
.flatMap(::videosFromElement)
}
"script" -> {
val scriptData = element.data().let {
when {
"eval(function" in it -> Unpacker.unpack(it)
else -> it
}
}.ifEmpty { null }?.replace("\\", "")
scriptData?.let(::videosFromScript).orEmpty()
}
"script" -> ScriptExtractor.videosFromScript(element.data(), headers)
"a" -> protectorExtractor.videosFromUrl(element.attr("href"))
else -> emptyList()
}
}
private fun videosFromScript(script: String): List<Video> {
return script.substringAfter("sources:").substringAfter(".src(")
.substringBefore(")")
.substringAfter("[")
.substringBefore("]")
.split("{")
.drop(1)
.map {
val quality = it.substringAfter("label", "")
.substringAfterKey()
.ifEmpty { name }
val url = it.substringAfter("file").substringAfter("src")
.substringAfterKey()
Video(url, quality, url, headers)
}
}
private val scriptSelectors = listOf("eval", "player.src", "this.src", "sources:")
.joinToString { "script:containsData($it):not(:containsData(/bg.mp4))" }
override fun videoListSelector() = "iframe, script:containsData(eval), script:containsData(player.src), script:containsData(this.src), script:containsData(sources:)"
override fun videoListSelector() = "iframe, $scriptSelectors"
override fun videoFromElement(element: Element): Video {
throw UnsupportedOperationException()
@ -285,7 +260,7 @@ class AnimesDigital : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
val entry = entryValues[index] as String
preferences.edit().putString(key, entry).commit()
}
}.let(screen::addPreference)
}.also(screen::addPreference)
}
// ============================= Utilities ==============================
@ -312,12 +287,6 @@ class AnimesDigital : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
}
}
private fun String.substringAfterKey() = substringAfter(":")
.substringAfter('"')
.substringBefore('"')
.substringAfter("'")
.substringBefore("'")
companion object {
const val PREFIX_SEARCH = "id:"

View File

@ -0,0 +1,22 @@
package eu.kanade.tachiyomi.animeextension.pt.animesdigital.extractors
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient
private const val HOST = "https://sabornutritivo.com"
class ProtectorExtractor(private val client: OkHttpClient) {
fun videosFromUrl(url: String): List<Video> {
val fixedUrl = if (!url.startsWith("https")) "https:$url" else url
val token = fixedUrl.toHttpUrl().queryParameter("token")!!
val headers = Headers.headersOf("cookie", "token=$token;")
val doc = client.newCall(GET("$HOST/social.php", headers)).execute().asJsoup()
val videoHeaders = Headers.headersOf("referer", doc.location())
val iframeUrl = doc.selectFirst("iframe")!!.attr("src").trim()
return listOf(Video(iframeUrl, "Animes Digital", iframeUrl, videoHeaders))
}
}

View File

@ -0,0 +1,37 @@
package eu.kanade.tachiyomi.animeextension.pt.animesdigital.extractors
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.lib.unpacker.Unpacker
import okhttp3.Headers
object ScriptExtractor {
fun videosFromScript(scriptData: String, headers: Headers): List<Video> {
val script = when {
"eval(function" in scriptData -> Unpacker.unpack(scriptData)
else -> scriptData
}.ifEmpty { null }?.replace("\\", "") ?: return emptyList()
return script.substringAfter("sources:").substringAfter(".src(")
.substringBefore(")")
.substringAfter("[")
.substringBefore("]")
.split("{")
.drop(1)
.map {
val quality = it.substringAfter("label", "")
.substringAfterKey()
.trim()
.ifEmpty { "Animes Digital" }
val url = it.substringAfter("file").substringAfter("src")
.substringAfterKey()
.trim()
Video(url, quality, url, headers)
}
}
private fun String.substringAfterKey() = substringAfter(':')
.substringAfter('"')
.substringBefore('"')
.substringAfter("'")
.substringBefore("'")
}