fix(pt/animesdigital): Fix video extractor (#2892)
This commit is contained in:
parent
566b9e43f0
commit
a77dcad9cf
@ -3,6 +3,8 @@ package eu.kanade.tachiyomi.animeextension.pt.animesdigital
|
|||||||
import android.app.Application
|
import android.app.Application
|
||||||
import androidx.preference.ListPreference
|
import androidx.preference.ListPreference
|
||||||
import androidx.preference.PreferenceScreen
|
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.ConfigurableAnimeSource
|
||||||
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
|
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
|
||||||
import eu.kanade.tachiyomi.animesource.model.AnimesPage
|
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.SEpisode
|
||||||
import eu.kanade.tachiyomi.animesource.model.Video
|
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.lib.unpacker.Unpacker
|
|
||||||
import eu.kanade.tachiyomi.network.GET
|
import eu.kanade.tachiyomi.network.GET
|
||||||
import eu.kanade.tachiyomi.network.POST
|
import eu.kanade.tachiyomi.network.POST
|
||||||
import eu.kanade.tachiyomi.network.awaitSuccess
|
import eu.kanade.tachiyomi.network.awaitSuccess
|
||||||
import eu.kanade.tachiyomi.util.asJsoup
|
import eu.kanade.tachiyomi.util.asJsoup
|
||||||
import eu.kanade.tachiyomi.util.parseAs
|
import eu.kanade.tachiyomi.util.parseAs
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import kotlinx.serialization.json.Json
|
|
||||||
import okhttp3.FormBody
|
import okhttp3.FormBody
|
||||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
@ -27,7 +27,6 @@ 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
|
|
||||||
|
|
||||||
class AnimesDigital : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
class AnimesDigital : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
||||||
|
|
||||||
@ -41,8 +40,6 @@ class AnimesDigital : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
|
|
||||||
override fun headersBuilder() = super.headersBuilder().add("Referer", baseUrl)
|
override fun headersBuilder() = super.headersBuilder().add("Referer", baseUrl)
|
||||||
|
|
||||||
private val json: Json by injectLazy()
|
|
||||||
|
|
||||||
private val preferences by lazy {
|
private val preferences by lazy {
|
||||||
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
|
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
|
||||||
}
|
}
|
||||||
@ -126,7 +123,7 @@ class AnimesDigital : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
override fun searchAnimeParse(response: Response): AnimesPage {
|
override fun searchAnimeParse(response: Response): AnimesPage {
|
||||||
return runCatching {
|
return runCatching {
|
||||||
val data = response.parseAs<SearchResponseDto>()
|
val data = response.parseAs<SearchResponseDto>()
|
||||||
val animes = data.results.map(Jsoup::parse)
|
val animes = data.results.map(Jsoup::parseBodyFragment)
|
||||||
.mapNotNull { it.selectFirst(searchAnimeSelector()) }
|
.mapNotNull { it.selectFirst(searchAnimeSelector()) }
|
||||||
.map(::searchAnimeFromElement)
|
.map(::searchAnimeFromElement)
|
||||||
val hasNext = data.total_page > data.page
|
val hasNext = data.total_page > data.page
|
||||||
@ -156,15 +153,15 @@ class AnimesDigital : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
else -> SAnime.UNKNOWN
|
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")
|
title = selectFirst("h1")!!.text()
|
||||||
author = infos.getInfo("Autor") ?: infos.getInfo("Diretor")
|
genre = select("div.genre a").eachText().joinToString()
|
||||||
|
|
||||||
title = infos.selectFirst("h1")!!.text()
|
description = selectFirst("div.sinopse")?.text()
|
||||||
genre = infos.select("div.genre a").eachText().joinToString()
|
}
|
||||||
|
|
||||||
description = infos.selectFirst("div.sinopse")?.text()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================== Episodes ==============================
|
// ============================== Episodes ==============================
|
||||||
@ -195,9 +192,9 @@ class AnimesDigital : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
episode_number = epname.substringAfterLast(" ").toFloatOrNull() ?: 1F
|
episode_number = epname.substringAfterLast(" ").toFloatOrNull() ?: 1F
|
||||||
name = buildString {
|
name = buildString {
|
||||||
append(epname)
|
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")) {
|
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> {
|
override fun videoListParse(response: Response): List<Video> {
|
||||||
val player = response.asJsoup().selectFirst("div#player")!!
|
val player = response.asJsoup().selectFirst("div#player")!!
|
||||||
return player.select("div.tab-video").flatMap { div ->
|
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 {
|
runCatching {
|
||||||
videosFromElement(element)
|
videosFromElement(element)
|
||||||
}.onFailure { it.printStackTrace() }.getOrElse { emptyList() }
|
}.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> {
|
private fun videosFromElement(element: Element): List<Video> {
|
||||||
return when (element.tagName()) {
|
return when (element.tagName()) {
|
||||||
"iframe" -> {
|
"iframe" -> {
|
||||||
val url = element.attr("data-lazy-src").ifEmpty { element.attr("src") }
|
val url = element.absUrl("data-lazy-src").ifEmpty { element.absUrl("src") }
|
||||||
.let {
|
|
||||||
when {
|
|
||||||
it.startsWith("/") -> baseUrl + it
|
|
||||||
else -> it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
client.newCall(GET(url, headers)).execute()
|
client.newCall(GET(url, headers)).execute()
|
||||||
.asJsoup()
|
.asJsoup()
|
||||||
.select(videoListSelector())
|
.select(videoListSelector())
|
||||||
.flatMap(::videosFromElement)
|
.flatMap(::videosFromElement)
|
||||||
}
|
}
|
||||||
"script" -> {
|
"script" -> ScriptExtractor.videosFromScript(element.data(), headers)
|
||||||
val scriptData = element.data().let {
|
"a" -> protectorExtractor.videosFromUrl(element.attr("href"))
|
||||||
when {
|
|
||||||
"eval(function" in it -> Unpacker.unpack(it)
|
|
||||||
else -> it
|
|
||||||
}
|
|
||||||
}.ifEmpty { null }?.replace("\\", "")
|
|
||||||
scriptData?.let(::videosFromScript).orEmpty()
|
|
||||||
}
|
|
||||||
else -> emptyList()
|
else -> emptyList()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun videosFromScript(script: String): List<Video> {
|
private val scriptSelectors = listOf("eval", "player.src", "this.src", "sources:")
|
||||||
return script.substringAfter("sources:").substringAfter(".src(")
|
.joinToString { "script:containsData($it):not(:containsData(/bg.mp4))" }
|
||||||
.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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
override fun videoFromElement(element: Element): Video {
|
||||||
throw UnsupportedOperationException()
|
throw UnsupportedOperationException()
|
||||||
@ -285,7 +260,7 @@ class AnimesDigital : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
val entry = entryValues[index] as String
|
val entry = entryValues[index] as String
|
||||||
preferences.edit().putString(key, entry).commit()
|
preferences.edit().putString(key, entry).commit()
|
||||||
}
|
}
|
||||||
}.let(screen::addPreference)
|
}.also(screen::addPreference)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================= Utilities ==============================
|
// ============================= Utilities ==============================
|
||||||
@ -312,12 +287,6 @@ class AnimesDigital : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun String.substringAfterKey() = substringAfter(":")
|
|
||||||
.substringAfter('"')
|
|
||||||
.substringBefore('"')
|
|
||||||
.substringAfter("'")
|
|
||||||
.substringBefore("'")
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val PREFIX_SEARCH = "id:"
|
const val PREFIX_SEARCH = "id:"
|
||||||
|
|
||||||
|
@ -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))
|
||||||
|
}
|
||||||
|
}
|
@ -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("'")
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user