fix(pt/anitube): Fixed pt/Anitube and pt/HinataSoul sources again #175

Merged
WebDitto merged 1 commit from fix/pt/anitube2 into main 2024-08-23 12:27:05 -05:00
6 changed files with 223 additions and 65 deletions

View file

@ -1,7 +1,7 @@
ext { ext {
extName = 'Anitube' extName = 'Anitube'
extClass = '.Anitube' extClass = '.Anitube'
extVersionCode = 16 extVersionCode = 17
} }
apply from: "$rootDir/common.gradle" apply from: "$rootDir/common.gradle"

View file

@ -1,6 +1,7 @@
package eu.kanade.tachiyomi.animeextension.pt.anitube package eu.kanade.tachiyomi.animeextension.pt.anitube
import android.app.Application import android.app.Application
import androidx.preference.EditTextPreference
import androidx.preference.ListPreference import androidx.preference.ListPreference
import androidx.preference.PreferenceScreen import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.animeextension.pt.anitube.extractors.AnitubeExtractor import eu.kanade.tachiyomi.animeextension.pt.anitube.extractors.AnitubeExtractor
@ -188,7 +189,7 @@ class Anitube : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
} }
// ============================ Video Links ============================= // ============================ 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 videoListParse(response: Response) = extractor.getVideoList(response)
override fun videoListSelector() = throw UnsupportedOperationException() override fun videoListSelector() = throw UnsupportedOperationException()
@ -210,7 +211,22 @@ class Anitube : 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)
// 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 ============================== // ============================= 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 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_KEY = "preferred_quality"
private const val PREF_QUALITY_TITLE = "Qualidade preferida" private const val PREF_QUALITY_TITLE = "Qualidade preferida"
private const val PREF_QUALITY_DEFAULT = "HD" private const val PREF_QUALITY_DEFAULT = "HD"

View file

@ -1,5 +1,6 @@
package eu.kanade.tachiyomi.animeextension.pt.anitube.extractors package eu.kanade.tachiyomi.animeextension.pt.anitube.extractors
import android.content.SharedPreferences
import android.util.Log import android.util.Log
import eu.kanade.tachiyomi.animesource.model.Video import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
@ -9,35 +10,83 @@ import okhttp3.FormBody
import okhttp3.Headers import okhttp3.Headers
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Response 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 fun getAdsUrl(
private var authCodeDate: Date = Calendar.getInstance().getTime() serverUrl: String,
thumbUrl: String,
link: String,
linkHeaders: Headers,
): String {
val videoName = serverUrl.split('/').last()
private fun getAuthCode(serverUrl: String, thumbUrl: String): String { val docLink = client.newCall(GET(link, headers = linkHeaders)).execute().asJsoup()
val duration = Calendar.getInstance().getTime().time - authCodeDate.time
// check the authCode in cache for 1 hour val refresh = docLink.selectFirst("meta[http-equiv=refresh]")?.attr("content")
if (authCodeCache.isNotBlank() && duration < 60 * 60 * 1000) {
Log.d("AnitubeExtractor", "Using authCode from cache") if (!refresh.isNullOrBlank()) {
return authCodeCache 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") Log.d("AnitubeExtractor", "Fetching new authCode")
val videoName = serverUrl.split('/').last()
val adsUrl = val adsUrl = getAdsUrl(serverUrl, thumbUrl, link, headers)
client.newCall(GET("$SITE_URL/playerricas.php?name=apphd/$videoName&img=$thumbUrl&url=$serverUrl"))
.execute()
.body.string()
.substringAfter("ADS_URL")
.substringAfter('"')
.substringBefore('"')
Log.d("AnitubeExtractor", "ADS URL: $adsUrl")
val adsContent = client.newCall(GET(adsUrl)).execute().body.string() val adsContent = client.newCall(GET(adsUrl)).execute().body.string()
val body = FormBody.Builder() val body = FormBody.Builder()
@ -66,11 +115,15 @@ class AnitubeExtractor(private val headers: Headers, private val client: OkHttpC
.substringBefore('"') .substringBefore('"')
if (publicidade.isBlank()) { if (publicidade.isBlank()) {
Log.e("AnitubeExtractor", "Failed to fetch \"publicidade\" code") Log.e(
return "" "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( client.newCall(
GET( GET(
"$ADS_URL/?token=$publicidade", "$ADS_URL/?token=$publicidade",
@ -83,13 +136,17 @@ class AnitubeExtractor(private val headers: Headers, private val client: OkHttpC
.substringAfter('"') .substringAfter('"')
.substringBefore('"') .substringBefore('"')
if (authCodeCache.isBlank()) { if (authCode.startsWith("?")) {
Log.e("AnitubeExtractor", "Failed to fetch auth code")
} else {
Log.d("AnitubeExtractor", "Auth code fetched successfully") 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<Video> { fun getVideoList(response: Response): List<Video> {
@ -110,7 +167,9 @@ class AnitubeExtractor(private val headers: Headers, private val client: OkHttpC
} }
} + listOf("appfullhd") } + listOf("appfullhd")
val authCode = getAuthCode(serverUrl, thumbUrl) val firstLink = doc.selectFirst("div.video_container > a, div.playerContainer > a")!!.attr("href")
val authCode = getAuthCode(serverUrl, thumbUrl, firstLink)
return qualities.mapIndexed { index, quality -> return qualities.mapIndexed { index, quality ->
val path = paths[index] val path = paths[index]
@ -120,7 +179,8 @@ class AnitubeExtractor(private val headers: Headers, private val client: OkHttpC
} }
companion object { companion object {
val ADS_URL = "https://ads.anitube.vip" private const val PREF_AUTHCODE_KEY = "authcode"
val SITE_URL = "https://www.anitube.vip" private const val ADS_URL = "https://ads.anitube.vip"
private const val SITE_URL = "https://www.anitube.vip"
} }
} }

View file

@ -1,7 +1,7 @@
ext { ext {
extName = 'Hinata Soul' extName = 'Hinata Soul'
extClass = '.HinataSoul' extClass = '.HinataSoul'
extVersionCode = 6 extVersionCode = 7
} }
apply from: "$rootDir/common.gradle" apply from: "$rootDir/common.gradle"

View file

@ -1,6 +1,7 @@
package eu.kanade.tachiyomi.animeextension.pt.hinatasoul package eu.kanade.tachiyomi.animeextension.pt.hinatasoul
import android.app.Application import android.app.Application
import androidx.preference.EditTextPreference
import androidx.preference.ListPreference import androidx.preference.ListPreference
import androidx.preference.PreferenceScreen import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.animeextension.pt.hinatasoul.extractors.HinataSoulExtractor import eu.kanade.tachiyomi.animeextension.pt.hinatasoul.extractors.HinataSoulExtractor
@ -162,7 +163,7 @@ class HinataSoul : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
} }
// ============================ Video Links ============================= // ============================ Video Links =============================
private val extractor by lazy { HinataSoulExtractor(headers, client) } private val extractor by lazy { HinataSoulExtractor(headers, client, preferences) }
override fun videoListParse(response: Response) = extractor.getVideoList(response) override fun videoListParse(response: Response) = extractor.getVideoList(response)
@ -186,6 +187,21 @@ class HinataSoul : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
preferences.edit().putString(key, entry).commit() preferences.edit().putString(key, entry).commit()
} }
}.also(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 ============================== // ============================= Utilities ==============================
@ -241,6 +257,9 @@ class HinataSoul : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
const val PREFIX_SEARCH = "slug:" const val PREFIX_SEARCH = "slug:"
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_KEY = "preferred_quality"
private const val PREF_QUALITY_TITLE = "Qualidade preferida" private const val PREF_QUALITY_TITLE = "Qualidade preferida"
private const val PREF_QUALITY_DEFAULT = "FULLHD" private const val PREF_QUALITY_DEFAULT = "FULLHD"

View file

@ -1,5 +1,6 @@
package eu.kanade.tachiyomi.animeextension.pt.hinatasoul.extractors package eu.kanade.tachiyomi.animeextension.pt.hinatasoul.extractors
import android.content.SharedPreferences
import android.util.Log import android.util.Log
import eu.kanade.tachiyomi.animesource.model.Video import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
@ -9,35 +10,83 @@ import okhttp3.FormBody
import okhttp3.Headers import okhttp3.Headers
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Response import okhttp3.Response
import java.util.Calendar
import java.util.Date
class HinataSoulExtractor(private val headers: Headers, private val client: OkHttpClient) { class HinataSoulExtractor(
private val headers: Headers,
private val client: OkHttpClient,
private val preferences: SharedPreferences,
) {
private var authCodeCache: String = "" private fun getAdsUrl(
private var authCodeDate: Date = Calendar.getInstance().getTime() serverUrl: String,
thumbUrl: String,
link: String,
linkHeaders: Headers,
): String {
val videoName = serverUrl.split('/').last()
private fun getAuthCode(serverUrl: String, thumbUrl: String): String { val docLink = client.newCall(GET(link, headers = linkHeaders)).execute().asJsoup()
val duration = Calendar.getInstance().getTime().time - authCodeDate.time
// check the authCode in cache for 1 hour val refresh = docLink.selectFirst("meta[http-equiv=refresh]")?.attr("content")
if (authCodeCache.isNotBlank() && duration < 60 * 60 * 1000) {
Log.d("HinataSoulExtractor", "Using authCode from cache") if (!refresh.isNullOrBlank()) {
return authCodeCache val newLink = refresh.substringAfter("=")
val newHeaders = linkHeaders.newBuilder().set("Referer", link).build()
Log.d("HinataSoulExtractor", "Following link redirection to $newLink")
return getAdsUrl(serverUrl, thumbUrl, newLink, newHeaders)
}
Log.d("HinataSoulExtractor", "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("HinataSoulExtractor", "ADS URL: $adsUrl")
return adsUrl
}
} catch (e: Exception) {
}
// Try default url
Log.e("HinataSoulExtractor", "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("HinataSoulExtractor", "AuthCode found in preferences")
val isSuccessful = client.newCall(GET("${serverUrl}$authCode", headers = headers))
.execute().isSuccessful
if (isSuccessful) {
Log.d("HinataSoulExtractor", "AuthCode is OK")
return authCode
}
Log.d("HinataSoulExtractor", "AuthCode is invalid")
} }
Log.d("HinataSoulExtractor", "Fetching new authCode") Log.d("HinataSoulExtractor", "Fetching new authCode")
val videoName = serverUrl.split('/').last()
val adsUrl = val adsUrl = getAdsUrl(serverUrl, thumbUrl, link, headers)
client.newCall(GET("$SITE_URL/playerricas.php?name=apphd/$videoName&img=$thumbUrl&url=$serverUrl"))
.execute()
.body.string()
.substringAfter("ADS_URL")
.substringAfter('"')
.substringBefore('"')
Log.d("HinataSoulExtractor", "ADS URL: $adsUrl")
val adsContent = client.newCall(GET(adsUrl)).execute().body.string() val adsContent = client.newCall(GET(adsUrl)).execute().body.string()
val body = FormBody.Builder() val body = FormBody.Builder()
@ -66,11 +115,15 @@ class HinataSoulExtractor(private val headers: Headers, private val client: OkHt
.substringBefore('"') .substringBefore('"')
if (publicidade.isBlank()) { if (publicidade.isBlank()) {
Log.e("HinataSoulExtractor", "Failed to fetch \"publicidade\" code") Log.e(
return "" "HinataSoulExtractor",
"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( client.newCall(
GET( GET(
"$ADS_URL/?token=$publicidade", "$ADS_URL/?token=$publicidade",
@ -83,13 +136,17 @@ class HinataSoulExtractor(private val headers: Headers, private val client: OkHt
.substringAfter('"') .substringAfter('"')
.substringBefore('"') .substringBefore('"')
if (authCodeCache.isBlank()) { if (authCode.startsWith("?")) {
Log.e("HinataSoulExtractor", "Failed to fetch auth code")
} else {
Log.d("HinataSoulExtractor", "Auth code fetched successfully") Log.d("HinataSoulExtractor", "Auth code fetched successfully")
preferences.edit().putString(PREF_AUTHCODE_KEY, authCode).commit()
} else {
Log.e(
"HinataSoulExtractor",
"Failed to fetch auth code, the current response: $authCode",
)
} }
return authCodeCache return authCode
} }
fun getVideoList(response: Response): List<Video> { fun getVideoList(response: Response): List<Video> {
@ -110,7 +167,9 @@ class HinataSoulExtractor(private val headers: Headers, private val client: OkHt
} }
} + listOf("appfullhd") } + listOf("appfullhd")
val authCode = getAuthCode(serverUrl, thumbUrl) val firstLink = doc.selectFirst("div.video_container > a, div.playerContainer > a")!!.attr("href")
val authCode = getAuthCode(serverUrl, thumbUrl, firstLink)
return qualities.mapIndexed { index, quality -> return qualities.mapIndexed { index, quality ->
val path = paths[index] val path = paths[index]
@ -120,7 +179,8 @@ class HinataSoulExtractor(private val headers: Headers, private val client: OkHt
} }
companion object { companion object {
val ADS_URL = "https://ads.anitube.vip" private const val PREF_AUTHCODE_KEY = "authcode"
val SITE_URL = "https://www.anitube.vip" private const val ADS_URL = "https://ads.anitube.vip"
private const val SITE_URL = "https://www.anitube.vip"
} }
} }