parent
8f42c60765
commit
36a21da8c9
2 changed files with 115 additions and 53 deletions
|
@ -1,7 +1,7 @@
|
||||||
ext {
|
ext {
|
||||||
extName = 'Anitube'
|
extName = 'Anitube'
|
||||||
extClass = '.Anitube'
|
extClass = '.Anitube'
|
||||||
extVersionCode = 23
|
extVersionCode = 24
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$rootDir/common.gradle"
|
apply from: "$rootDir/common.gradle"
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package eu.kanade.tachiyomi.animeextension.pt.anitube.extractors
|
package eu.kanade.tachiyomi.animeextension.pt.anitube.extractors
|
||||||
|
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
|
import android.util.Base64
|
||||||
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
|
||||||
|
@ -14,6 +15,9 @@ import okhttp3.OkHttpClient
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import org.jsoup.nodes.Document
|
import org.jsoup.nodes.Document
|
||||||
import java.net.ProtocolException
|
import java.net.ProtocolException
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.Calendar
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
class AnitubeExtractor(
|
class AnitubeExtractor(
|
||||||
private val headers: Headers,
|
private val headers: Headers,
|
||||||
|
@ -132,32 +136,86 @@ class AnitubeExtractor(
|
||||||
return "https://widgets.outbrain.com/outbrain.js"
|
return "https://widgets.outbrain.com/outbrain.js"
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getAuthCode(serverUrl: String, thumbUrl: String, link: String): String {
|
private fun getAuthCode(serverUrl: String, thumbUrl: String, link: String): String? {
|
||||||
var authCode = preferences.getString(PREF_AUTHCODE_KEY, "")!!
|
try {
|
||||||
|
var authCode = preferences.getString(PREF_AUTHCODE_KEY, "")!!
|
||||||
|
|
||||||
if (authCode.isNotBlank()) {
|
if (authCode.isNotBlank()) {
|
||||||
Log.d(tag, "AuthCode found in preferences")
|
Log.d(tag, "AuthCode found in preferences")
|
||||||
|
|
||||||
val response = checkVideoExists("${serverUrl}$authCode")
|
val authArgs = Base64.decode(authCode.substringAfter("="), Base64.DEFAULT).let(::String)
|
||||||
|
val url = "$serverUrl?$authArgs".toHttpUrl()
|
||||||
|
|
||||||
if (response.exists || response.code == 500) {
|
val serverTime =
|
||||||
Log.d(tag, "AuthCode is OK")
|
SimpleDateFormat("M/d/yyyy h:m:s a", Locale.ENGLISH).parse(
|
||||||
|
url.queryParameter("server_time") ?: "",
|
||||||
|
)
|
||||||
|
|
||||||
|
val calendar = Calendar.getInstance()
|
||||||
|
serverTime?.let { calendar.setTime(it) }
|
||||||
|
|
||||||
|
url.queryParameter("validminutes")?.toInt()
|
||||||
|
?.let { calendar.add(Calendar.MINUTE, it) }
|
||||||
|
|
||||||
|
if (Calendar.getInstance() < calendar) {
|
||||||
|
Log.d(tag, "AuthCode is OK")
|
||||||
|
return authCode
|
||||||
|
}
|
||||||
|
Log.d(tag, "AuthCode is invalid")
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.d(tag, "Fetching new authCode")
|
||||||
|
|
||||||
|
val adsUrl = getAdsUrl(serverUrl, thumbUrl, link, headers)
|
||||||
|
|
||||||
|
val adsContent = client.newCall(GET(adsUrl)).execute().body.string()
|
||||||
|
|
||||||
|
val body = FormBody.Builder()
|
||||||
|
.add("category", "client")
|
||||||
|
.add("type", "premium")
|
||||||
|
.add("ad", adsContent)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
val newHeaders = headers.newBuilder()
|
||||||
|
.set("Referer", "https://${SITE_URL.toHttpUrl().host}/")
|
||||||
|
.add("Accept", "*/*")
|
||||||
|
.add("Cache-Control", "no-cache")
|
||||||
|
.add("Pragma", "no-cache")
|
||||||
|
.add("Connection", "keep-alive")
|
||||||
|
.add("Sec-Fetch-Dest", "empty")
|
||||||
|
.add("Sec-Fetch-Mode", "cors")
|
||||||
|
.add("Sec-Fetch-Site", "same-site")
|
||||||
|
.build()
|
||||||
|
|
||||||
|
authCode =
|
||||||
|
client.newCall(POST(ADS_URL, headers = newHeaders, body = body))
|
||||||
|
.execute()
|
||||||
|
.body.string()
|
||||||
|
.substringAfter("\"publicidade\"")
|
||||||
|
.substringAfter('"')
|
||||||
|
.substringBefore('"')
|
||||||
|
|
||||||
|
if (authCode.startsWith("?wmsAuthSign=")) {
|
||||||
|
Log.d(tag, "Auth code fetched successfully")
|
||||||
|
preferences.edit().putString(PREF_AUTHCODE_KEY, authCode).commit()
|
||||||
return authCode
|
return authCode
|
||||||
}
|
}
|
||||||
Log.d(tag, "AuthCode is invalid")
|
|
||||||
|
Log.e(
|
||||||
|
tag,
|
||||||
|
"Failed to fetch \"publicidade\" code, the current response: $authCode",
|
||||||
|
)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(tag, e.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.d(tag, "Fetching new authCode")
|
preferences.edit().putString(PREF_AUTHCODE_KEY, "").commit()
|
||||||
|
|
||||||
val adsUrl = getAdsUrl(serverUrl, thumbUrl, link, headers)
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
val adsContent = client.newCall(GET(adsUrl)).execute().body.string()
|
private fun getVideoToken(videoUrl: String, authCode: String?): String {
|
||||||
|
val token = authCode ?: "undefined"
|
||||||
val body = FormBody.Builder()
|
|
||||||
.add("category", "client")
|
|
||||||
.add("type", "premium")
|
|
||||||
.add("ad", adsContent)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
val newHeaders = headers.newBuilder()
|
val newHeaders = headers.newBuilder()
|
||||||
.set("Referer", "https://${SITE_URL.toHttpUrl().host}/")
|
.set("Referer", "https://${SITE_URL.toHttpUrl().host}/")
|
||||||
|
@ -170,27 +228,10 @@ class AnitubeExtractor(
|
||||||
.add("Sec-Fetch-Site", "same-site")
|
.add("Sec-Fetch-Site", "same-site")
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
val publicidade =
|
val videoToken =
|
||||||
client.newCall(POST(ADS_URL, headers = newHeaders, body = body))
|
|
||||||
.execute()
|
|
||||||
.body.string()
|
|
||||||
.substringAfter("\"publicidade\"")
|
|
||||||
.substringAfter('"')
|
|
||||||
.substringBefore('"')
|
|
||||||
|
|
||||||
if (publicidade.isBlank()) {
|
|
||||||
Log.e(
|
|
||||||
tag,
|
|
||||||
"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")
|
|
||||||
}
|
|
||||||
|
|
||||||
authCode =
|
|
||||||
client.newCall(
|
client.newCall(
|
||||||
GET(
|
GET(
|
||||||
"$ADS_URL?token=$publicidade",
|
"$ADS_URL?token=$token&url=$videoUrl",
|
||||||
headers = newHeaders,
|
headers = newHeaders,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -200,25 +241,25 @@ class AnitubeExtractor(
|
||||||
.substringAfter('"')
|
.substringAfter('"')
|
||||||
.substringBefore('"')
|
.substringBefore('"')
|
||||||
|
|
||||||
if (authCode.startsWith("?")) {
|
if (videoToken.startsWith("?")) {
|
||||||
Log.d(tag, "Auth code fetched successfully")
|
Log.d(tag, "Video token fetched successfully")
|
||||||
preferences.edit().putString(PREF_AUTHCODE_KEY, authCode).commit()
|
return videoToken
|
||||||
} else {
|
|
||||||
Log.e(
|
|
||||||
tag,
|
|
||||||
"Failed to fetch auth code, the current response: $authCode",
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return authCode
|
Log.e(
|
||||||
|
tag,
|
||||||
|
"Failed to fetch video token, the current response: $videoToken",
|
||||||
|
)
|
||||||
|
|
||||||
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getVideoList(doc: Document): List<Video> {
|
fun getVideoList(doc: Document): List<Video> {
|
||||||
|
Log.d(tag, "Starting to fetch video list")
|
||||||
|
|
||||||
val hasFHD = doc.selectFirst("div.abaItem:contains(FULLHD)") != null
|
val hasFHD = doc.selectFirst("div.abaItem:contains(FULLHD)") != null
|
||||||
val serverUrl = doc.selectFirst("meta[itemprop=contentURL]")!!
|
val serverUrl = doc.selectFirst("meta[itemprop=contentURL]")!!
|
||||||
.attr("content")
|
.attr("content")
|
||||||
.replace("cdn1", "cdn3")
|
|
||||||
.replace("cdn80", "cdn8")
|
|
||||||
val thumbUrl = doc.selectFirst("meta[itemprop=thumbnailUrl]")!!
|
val thumbUrl = doc.selectFirst("meta[itemprop=thumbnailUrl]")!!
|
||||||
.attr("content")
|
.attr("content")
|
||||||
val type = serverUrl.split("/").get(3)
|
val type = serverUrl.split("/").get(3)
|
||||||
|
@ -231,6 +272,8 @@ class AnitubeExtractor(
|
||||||
}
|
}
|
||||||
} + listOf("appfullhd")
|
} + listOf("appfullhd")
|
||||||
|
|
||||||
|
Log.d(tag, "Found ${paths.size} videos")
|
||||||
|
|
||||||
val firstLink =
|
val firstLink =
|
||||||
doc.selectFirst("div.video_container > a, div.playerContainer > a")!!.attr("href")
|
doc.selectFirst("div.video_container > a, div.playerContainer > a")!!.attr("href")
|
||||||
|
|
||||||
|
@ -240,16 +283,35 @@ class AnitubeExtractor(
|
||||||
.mapIndexed { index, quality ->
|
.mapIndexed { index, quality ->
|
||||||
object {
|
object {
|
||||||
var path = paths[index]
|
var path = paths[index]
|
||||||
var url = serverUrl.replace(type, path) + authCode
|
var url = serverUrl.replace(type, path)
|
||||||
var quality = "$quality - Anitube"
|
var quality = "$quality - Anitube"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.parallelCatchingFlatMapBlocking {
|
.parallelCatchingFlatMapBlocking {
|
||||||
if (!checkVideoExists(it.url).exists) {
|
if (!authCode.isNullOrBlank() && checkVideoExists(it.url + authCode).exists) {
|
||||||
Log.d(tag, "Video not exists: ${it.url.substringBefore("?")}")
|
return@parallelCatchingFlatMapBlocking listOf(
|
||||||
return@parallelCatchingFlatMapBlocking emptyList()
|
Video(
|
||||||
|
it.url + authCode,
|
||||||
|
it.quality,
|
||||||
|
it.url + authCode,
|
||||||
|
headers = headers,
|
||||||
|
),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
listOf(Video(it.url, it.quality, it.url, headers = headers))
|
val videoToken = getVideoToken(it.url, authCode)
|
||||||
|
if (videoToken.isNotBlank() && checkVideoExists(it.url + videoToken).exists) {
|
||||||
|
return@parallelCatchingFlatMapBlocking listOf(
|
||||||
|
Video(
|
||||||
|
it.url + videoToken,
|
||||||
|
it.quality,
|
||||||
|
it.url + videoToken,
|
||||||
|
headers = headers,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.d(tag, "Video not exists: ${it.url.substringBefore("?")}")
|
||||||
|
return@parallelCatchingFlatMapBlocking emptyList()
|
||||||
}
|
}
|
||||||
.reversed()
|
.reversed()
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue