Skip to content

Commit

Permalink
Fix(pt/Anitube): Update source (#104)
Browse files Browse the repository at this point in the history
* Fix(pt/Anitube): Update source
  • Loading branch information
Dark25 authored Oct 10, 2024
1 parent e1f6145 commit 5469cd0
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 264 deletions.
2 changes: 1 addition & 1 deletion src/pt/anitube/build.gradle
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
ext {
extName = 'Anitube'
extClass = '.Anitube'
extVersionCode = 17
extVersionCode = 18
}

apply from: "$rootDir/common.gradle"
Original file line number Diff line number Diff line change
@@ -1,26 +1,31 @@
package eu.kanade.tachiyomi.animeextension.pt.anitube

import android.app.Application
import androidx.preference.EditTextPreference
import androidx.preference.ListPreference
import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.animeextension.pt.anitube.extractors.AnitubeExtractor
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
import eu.kanade.tachiyomi.animesource.model.AnimesPage
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.network.GET
import eu.kanade.tachiyomi.network.awaitSuccess
import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.decodeFromStream
import okhttp3.FormBody
import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
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
import java.text.SimpleDateFormat
import java.util.Locale

Expand All @@ -38,8 +43,10 @@ class Anitube : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
}

private val json: Json by injectLazy()

override fun headersBuilder() = super.headersBuilder()
.add("Referer", "$baseUrl/")
.add("Referer", baseUrl)
.add("Accept-Language", ACCEPT_LANGUAGE)

// ============================== Popular ===============================
Expand Down Expand Up @@ -79,30 +86,6 @@ class Anitube : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override fun latestUpdatesNextPageSelector() = popularAnimeNextPageSelector()

// =============================== Search ===============================
override suspend fun getSearchAnime(
page: Int,
query: String,
filters: AnimeFilterList,
): AnimesPage {
return if (query.startsWith(PREFIX_SEARCH)) {
val path = query.removePrefix(PREFIX_SEARCH)
client.newCall(GET("$baseUrl/$path"))
.awaitSuccess()
.use(::searchAnimeByIdParse)
} else {
super.getSearchAnime(page, query, filters)
}
}

private fun searchAnimeByIdParse(response: Response): AnimesPage {
val details = animeDetailsParse(response).apply {
setUrlWithoutDomain(response.request.url.toString())
initialized = true
}

return AnimesPage(listOf(details), false)
}

override fun getFilterList(): AnimeFilterList = AnitubeFilters.FILTER_LIST

override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
Expand All @@ -114,14 +97,7 @@ class Anitube : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
val char = params.initialChar
when {
season.isNotBlank() -> "$baseUrl/temporada/$season/$year"
genre.isNotBlank() ->
"$baseUrl/genero/$genre/page/$page/${
char.replace(
"todos",
"",
)
}"

genre.isNotBlank() -> "$baseUrl/genero/$genre/page/$page/${char.replace("todos", "")}"
else -> "$baseUrl/anime/page/$page/letra/$char"
}
} else {
Expand All @@ -143,7 +119,7 @@ class Anitube : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
val infos = content.selectFirst("div.anime_infos")!!

title = doc.selectFirst("div.anime_container_titulo")!!.text()
thumbnail_url = content.selectFirst("img")?.attr("src")
thumbnail_url = content.selectFirst("img")?.imgAttr()
genre = infos.getInfo("Gêneros")
author = infos.getInfo("Autor")
artist = infos.getInfo("Estúdio")
Expand All @@ -159,6 +135,14 @@ class Anitube : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
}
}

fun Element.imgAttr(): String = when {
hasAttr("data-cfsrc") -> absUrl("data-cfsrc")
hasAttr("data-lazy-src") -> absUrl("data-lazy-src")
hasAttr("data-src") -> absUrl("data-src").substringBefore(" ")
hasAttr("srcset") -> absUrl("srcset").substringBefore(" ")
else -> absUrl("src")
}

// ============================== Episodes ==============================
override fun episodeListSelector() = "div.animepag_episodios_item > a"

Expand All @@ -179,19 +163,81 @@ class Anitube : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override fun episodeFromElement(element: Element) = SEpisode.create().apply {
setUrlWithoutDomain(element.attr("href"))
episode_number = element.selectFirst("div.animepag_episodios_item_views")!!
.text()
.text().trim()
.substringAfter(" ")
.toFloatOrNull() ?: 0F
name = element.selectFirst("div.animepag_episodios_item_nome")!!.text()
name = element.selectFirst("div[class*='animepag_episodios_item_views']")!!.text()
date_upload = element.selectFirst("div.animepag_episodios_item_date")!!
.text()
.toDate()
}

// ============================ Video Links =============================
private val extractor by lazy { AnitubeExtractor(headers, client, preferences) }
override fun videoListParse(response: Response) = AnitubeExtractor.getVideoList(response, headers).let {
val auth = getToken()

it.map { video ->
Video(
url = video.url,
quality = video.quality,
videoUrl = "${video.videoUrl}${auth.value}",
headers = video.headers,
subtitleTracks = video.subtitleTracks,
audioTracks = video.audioTracks,
)
}
}

private fun getToken(): AnitubeToken {
val headers = Headers.Builder()
.set("Accept", "*/*")
.set("Accept-Encoding", "br, zstd")
.set("Accept-Language", "pt-BR,en-US;q=0.7,en;q=0.3")
.set("Cache-Control", "no-cache")
.set("Connection", "keep-alive")
.set("Pragma", "no-cache")
.set("Referer", "$baseUrl/")
.set("Sec-Fetch-Dest", "empty")
.set("Sec-Fetch-Mode", "cors")
.set("Sec-Fetch-Site", "same-site")
.apply {
headers["User-Agent"]?.let {
set("User-Agent", it)
}
}
.build()

val client = OkHttpClient()

val ads = client.newCall(GET("https://widgets.outbrain.com/outbrain.js", headers))
.execute()

val form = FormBody.Builder()
.add("category", "client")
.add("type", "premium")
.add("ad", ads.body.string())
.build()

val response = client
.newCall(POST(url = "https://ads.anitube.vip", headers = headers, body = form))
.execute()

val token = response.parseAs<List<AnitubeToken>>().first()

val tokenUrl = "https://ads.anitube.vip".toHttpUrl().newBuilder()
.addQueryParameter("token", token.value)
.build()

return client.newCall(GET(tokenUrl, headers))
.execute()
.parseAs<List<AnitubeToken>>()
.first()
}

private inline fun <reified T> Response.parseAs(): T = use {
json.decodeFromStream(it.body.byteStream())
}

override fun videoListParse(response: Response) = extractor.getVideoList(response)
override fun videoListSelector() = throw UnsupportedOperationException()
override fun videoFromElement(element: Element) = throw UnsupportedOperationException()
override fun videoUrlParse(document: Document) = throw UnsupportedOperationException()
Expand All @@ -211,22 +257,7 @@ class Anitube : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
val entry = entryValues[index] as String
preferences.edit().putString(key, entry).commit()
}
}.also(screen::addPreference)

// Auth Code
EditTextPreference(screen.context).apply {
key = PREF_AUTHCODE_KEY
title = "Auth Code"
setDefaultValue(PREF_AUTHCODE_DEFAULT)
summary = PREF_AUTHCODE_SUMMARY

setOnPreferenceChangeListener { _, newValue ->
runCatching {
val value = (newValue as String).trim().ifBlank { PREF_AUTHCODE_DEFAULT }
preferences.edit().putString(key, value).commit()
}.getOrDefault(false)
}
}.also(screen::addPreference)
}.let(screen::addPreference)
}

// ============================= Utilities ==============================
Expand Down Expand Up @@ -275,14 +306,10 @@ class Anitube : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
}

companion object {
const val PREFIX_SEARCH = "id:"
private val DATE_FORMATTER by lazy { SimpleDateFormat("dd/MM/yyyy", Locale.ENGLISH) }

private const val ACCEPT_LANGUAGE = "pt-BR,pt;q=0.9,en-US;q=0.8,en;q=0.7"

private const val PREF_AUTHCODE_KEY = "authcode"
private const val PREF_AUTHCODE_SUMMARY = "Código de Autenticação"
private const val PREF_AUTHCODE_DEFAULT = ""
private const val PREF_QUALITY_KEY = "preferred_quality"
private const val PREF_QUALITY_TITLE = "Qualidade preferida"
private const val PREF_QUALITY_DEFAULT = "HD"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package eu.kanade.tachiyomi.animeextension.pt.anitube

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
class AnitubeToken(
@SerialName("publicidade")
val value: String,
)

This file was deleted.

Loading

0 comments on commit 5469cd0

Please sign in to comment.