diff --git a/src/pt/anitube/build.gradle b/src/pt/anitube/build.gradle index 9b47e747..39b6693e 100644 --- a/src/pt/anitube/build.gradle +++ b/src/pt/anitube/build.gradle @@ -1,7 +1,7 @@ ext { extName = 'Anitube' extClass = '.Anitube' - extVersionCode = 16 + extVersionCode = 17 } apply from: "$rootDir/common.gradle" diff --git a/src/pt/anitube/src/eu/kanade/tachiyomi/animeextension/pt/anitube/Anitube.kt b/src/pt/anitube/src/eu/kanade/tachiyomi/animeextension/pt/anitube/Anitube.kt index 63937fde..953b5f7e 100644 --- a/src/pt/anitube/src/eu/kanade/tachiyomi/animeextension/pt/anitube/Anitube.kt +++ b/src/pt/anitube/src/eu/kanade/tachiyomi/animeextension/pt/anitube/Anitube.kt @@ -1,6 +1,7 @@ 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 @@ -188,7 +189,7 @@ class Anitube : ConfigurableAnimeSource, ParsedAnimeHttpSource() { } // ============================ Video Links ============================= - private val extractor by lazy { AnitubeExtractor(headers, client) } + private val extractor by lazy { AnitubeExtractor(headers, client, preferences) } override fun videoListParse(response: Response) = extractor.getVideoList(response) override fun videoListSelector() = throw UnsupportedOperationException() @@ -210,7 +211,22 @@ class Anitube : ConfigurableAnimeSource, ParsedAnimeHttpSource() { val entry = entryValues[index] as String preferences.edit().putString(key, entry).commit() } - }.let(screen::addPreference) + }.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) } // ============================= Utilities ============================== @@ -264,6 +280,9 @@ class Anitube : ConfigurableAnimeSource, ParsedAnimeHttpSource() { 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" diff --git a/src/pt/anitube/src/eu/kanade/tachiyomi/animeextension/pt/anitube/extractors/AnitubeExtractor.kt b/src/pt/anitube/src/eu/kanade/tachiyomi/animeextension/pt/anitube/extractors/AnitubeExtractor.kt index 7d218de9..1eeb7411 100644 --- a/src/pt/anitube/src/eu/kanade/tachiyomi/animeextension/pt/anitube/extractors/AnitubeExtractor.kt +++ b/src/pt/anitube/src/eu/kanade/tachiyomi/animeextension/pt/anitube/extractors/AnitubeExtractor.kt @@ -1,5 +1,6 @@ package eu.kanade.tachiyomi.animeextension.pt.anitube.extractors +import android.content.SharedPreferences import android.util.Log import eu.kanade.tachiyomi.animesource.model.Video import eu.kanade.tachiyomi.network.GET @@ -9,35 +10,83 @@ import okhttp3.FormBody import okhttp3.Headers import okhttp3.OkHttpClient import okhttp3.Response -import java.util.Calendar -import java.util.Date -class AnitubeExtractor(private val headers: Headers, private val client: OkHttpClient) { +class AnitubeExtractor( + private val headers: Headers, + private val client: OkHttpClient, + private val preferences: SharedPreferences, +) { - private var authCodeCache: String = "" - private var authCodeDate: Date = Calendar.getInstance().getTime() + private fun getAdsUrl( + serverUrl: String, + thumbUrl: String, + link: String, + linkHeaders: Headers, + ): String { + val videoName = serverUrl.split('/').last() - private fun getAuthCode(serverUrl: String, thumbUrl: String): String { - val duration = Calendar.getInstance().getTime().time - authCodeDate.time + val docLink = client.newCall(GET(link, headers = linkHeaders)).execute().asJsoup() - // check the authCode in cache for 1 hour - if (authCodeCache.isNotBlank() && duration < 60 * 60 * 1000) { - Log.d("AnitubeExtractor", "Using authCode from cache") - return authCodeCache + val refresh = docLink.selectFirst("meta[http-equiv=refresh]")?.attr("content") + + if (!refresh.isNullOrBlank()) { + val newLink = refresh.substringAfter("=") + val newHeaders = linkHeaders.newBuilder().set("Referer", link).build() + Log.d("AnitubeExtractor", "Following link redirection to $newLink") + + return getAdsUrl(serverUrl, thumbUrl, newLink, newHeaders) + } + + Log.d("AnitubeExtractor", "Fetching ADS URL") + + val newHeaders = linkHeaders.newBuilder().set("Referer", link).build() + + try { + val adsUrl = + client.newCall( + GET( + "$SITE_URL/playerricas.php?name=apphd/$videoName&img=$thumbUrl&url=$serverUrl", + headers = newHeaders, + ), + ) + .execute() + .body.string() + .substringAfter("ADS_URL") + .substringAfter('"') + .substringBefore('"') + + if (adsUrl.startsWith("http")) { + Log.d("AnitubeExtractor", "ADS URL: $adsUrl") + return adsUrl + } + } catch (e: Exception) { + } + + // Try default url + Log.e("AnitubeExtractor", "Failed to get the ADS URL, trying the default") + return "https://www.popads.net/js/adblock.js" + } + + private fun getAuthCode(serverUrl: String, thumbUrl: String, link: String): String { + var authCode = preferences.getString(PREF_AUTHCODE_KEY, "")!! + + if (authCode.isNotBlank()) { + Log.d("AnitubeExtractor", "AuthCode found in preferences") + + val isSuccessful = client.newCall(GET("${serverUrl}$authCode", headers = headers)) + .execute().isSuccessful + + if (isSuccessful) { + Log.d("AnitubeExtractor", "AuthCode is OK") + return authCode + } + Log.d("AnitubeExtractor", "AuthCode is invalid") } Log.d("AnitubeExtractor", "Fetching new authCode") - val videoName = serverUrl.split('/').last() - val adsUrl = - client.newCall(GET("$SITE_URL/playerricas.php?name=apphd/$videoName&img=$thumbUrl&url=$serverUrl")) - .execute() - .body.string() - .substringAfter("ADS_URL") - .substringAfter('"') - .substringBefore('"') + val adsUrl = getAdsUrl(serverUrl, thumbUrl, link, headers) - Log.d("AnitubeExtractor", "ADS URL: $adsUrl") val adsContent = client.newCall(GET(adsUrl)).execute().body.string() val body = FormBody.Builder() @@ -66,11 +115,15 @@ class AnitubeExtractor(private val headers: Headers, private val client: OkHttpC .substringBefore('"') if (publicidade.isBlank()) { - Log.e("AnitubeExtractor", "Failed to fetch \"publicidade\" code") - return "" + Log.e( + "AnitubeExtractor", + "Failed to fetch \"publicidade\" code, the current response: $publicidade", + ) + + throw Exception("Por favor, abra o vídeo uma vez no navegador para liberar o IP") } - authCodeCache = + authCode = client.newCall( GET( "$ADS_URL/?token=$publicidade", @@ -83,13 +136,17 @@ class AnitubeExtractor(private val headers: Headers, private val client: OkHttpC .substringAfter('"') .substringBefore('"') - if (authCodeCache.isBlank()) { - Log.e("AnitubeExtractor", "Failed to fetch auth code") - } else { + if (authCode.startsWith("?")) { Log.d("AnitubeExtractor", "Auth code fetched successfully") + preferences.edit().putString(PREF_AUTHCODE_KEY, authCode).commit() + } else { + Log.e( + "AnitubeExtractor", + "Failed to fetch auth code, the current response: $authCode", + ) } - return authCodeCache + return authCode } fun getVideoList(response: Response): List