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

This commit is contained in:
WebDitto 2024-08-21 21:15:27 -03:00 committed by GitHub
parent 5ddb5f061b
commit 36ebc198d3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 211 additions and 45 deletions

View file

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

View file

@ -38,7 +38,7 @@ class Anitube : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
}
override fun headersBuilder() = super.headersBuilder()
.add("Referer", baseUrl)
.add("Referer", "$baseUrl/")
.add("Accept-Language", ACCEPT_LANGUAGE)
// ============================== Popular ===============================
@ -78,7 +78,11 @@ class Anitube : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override fun latestUpdatesNextPageSelector() = popularAnimeNextPageSelector()
// =============================== Search ===============================
override suspend fun getSearchAnime(page: Int, query: String, filters: AnimeFilterList): AnimesPage {
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"))
@ -97,6 +101,7 @@ class Anitube : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
return AnimesPage(listOf(details), false)
}
override fun getFilterList(): AnimeFilterList = AnitubeFilters.FILTER_LIST
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
@ -108,7 +113,14 @@ 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 {
@ -176,7 +188,9 @@ class Anitube : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
}
// ============================ Video Links =============================
override fun videoListParse(response: Response) = AnitubeExtractor.getVideoList(response, headers, client)
private val extractor by lazy { AnitubeExtractor(headers, client) }
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()

View file

@ -1,5 +1,6 @@
package eu.kanade.tachiyomi.animeextension.pt.anitube.extractors
import android.util.Log
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST
@ -8,9 +9,90 @@ import okhttp3.FormBody
import okhttp3.Headers
import okhttp3.OkHttpClient
import okhttp3.Response
import java.util.Calendar
import java.util.Date
object AnitubeExtractor {
fun getVideoList(response: Response, headers: Headers, client: OkHttpClient): List<Video> {
class AnitubeExtractor(private val headers: Headers, private val client: OkHttpClient) {
private var authCodeCache: String = ""
private var authCodeDate: Date = Calendar.getInstance().getTime()
private fun getAuthCode(serverUrl: String, thumbUrl: String): String {
val duration = Calendar.getInstance().getTime().time - authCodeDate.time
// check the authCode in cache for 1 hour
if (authCodeCache.isNotBlank() && duration < 60 * 60 * 1000) {
Log.d("AnitubeExtractor", "Using authCode from cache")
return authCodeCache
}
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('"')
Log.d("AnitubeExtractor", "ADS URL: $adsUrl")
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", SITE_URL)
.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()
val publicidade =
client.newCall(POST("$ADS_URL/", headers = newHeaders, body = body))
.execute()
.body.string()
.substringAfter("\"publicidade\"")
.substringAfter('"')
.substringBefore('"')
if (publicidade.isBlank()) {
Log.e("AnitubeExtractor", "Failed to fetch \"publicidade\" code")
return ""
}
authCodeCache =
client.newCall(
GET(
"$ADS_URL/?token=$publicidade",
headers = newHeaders,
),
)
.execute()
.body.string()
.substringAfter("\"publicidade\"")
.substringAfter('"')
.substringBefore('"')
if (authCodeCache.isBlank()) {
Log.e("AnitubeExtractor", "Failed to fetch auth code")
} else {
Log.d("AnitubeExtractor", "Auth code fetched successfully")
}
return authCodeCache
}
fun getVideoList(response: Response): List<Video> {
val doc = response.asJsoup()
val hasFHD = doc.selectFirst("div.abaItem:contains(FULLHD)") != null
val serverUrl = doc.selectFirst("meta[itemprop=contentURL]")!!
@ -28,37 +110,7 @@ object AnitubeExtractor {
}
} + listOf("appfullhd")
val videoName = serverUrl.split('/').last()
val adsUrl =
client.newCall(GET("https://www.anitube.vip/playerricas.php?name=apphd/$videoName&img=$thumbUrl&url=$serverUrl"))
.execute()
.body.string()
.substringAfter("ADS_URL")
.substringAfter('"')
.substringBefore('"')
val adsContent = client.newCall(GET(adsUrl)).execute().body.string()
val body = FormBody.Builder()
.add("category", "client")
.add("type", "premium")
.add("ad", adsContent)
.build()
val publicidade = client.newCall(POST("https://ads.anitube.vip/", body = body))
.execute()
.body.string()
.substringAfter("\"publicidade\"")
.substringAfter('"')
.substringBefore('"')
val authCode = client.newCall(GET("https://ads.anitube.vip/?token=$publicidade"))
.execute()
.body.string()
.substringAfter("\"publicidade\"")
.substringAfter('"')
.substringBefore('"')
val authCode = getAuthCode(serverUrl, thumbUrl)
return qualities.mapIndexed { index, quality ->
val path = paths[index]
@ -66,4 +118,9 @@ object AnitubeExtractor {
Video(url, quality, url, headers = headers)
}.reversed()
}
companion object {
val ADS_URL = "https://ads.anitube.vip"
val SITE_URL = "https://www.anitube.vip"
}
}

View file

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

View file

@ -162,7 +162,7 @@ class HinataSoul : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
}
// ============================ Video Links =============================
private val extractor by lazy { HinataSoulExtractor(headers) }
private val extractor by lazy { HinataSoulExtractor(headers, client) }
override fun videoListParse(response: Response) = extractor.getVideoList(response)

View file

@ -1,11 +1,96 @@
package eu.kanade.tachiyomi.animeextension.pt.hinatasoul.extractors
import android.util.Log
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.FormBody
import okhttp3.Headers
import okhttp3.OkHttpClient
import okhttp3.Response
import java.util.Calendar
import java.util.Date
class HinataSoulExtractor(private val headers: Headers) {
class HinataSoulExtractor(private val headers: Headers, private val client: OkHttpClient) {
private var authCodeCache: String = ""
private var authCodeDate: Date = Calendar.getInstance().getTime()
private fun getAuthCode(serverUrl: String, thumbUrl: String): String {
val duration = Calendar.getInstance().getTime().time - authCodeDate.time
// check the authCode in cache for 1 hour
if (authCodeCache.isNotBlank() && duration < 60 * 60 * 1000) {
Log.d("HinataSoulExtractor", "Using authCode from cache")
return authCodeCache
}
Log.d("HinataSoulExtractor", "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('"')
Log.d("HinataSoulExtractor", "ADS URL: $adsUrl")
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", SITE_URL)
.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()
val publicidade =
client.newCall(POST("$ADS_URL/", headers = newHeaders, body = body))
.execute()
.body.string()
.substringAfter("\"publicidade\"")
.substringAfter('"')
.substringBefore('"')
if (publicidade.isBlank()) {
Log.e("HinataSoulExtractor", "Failed to fetch \"publicidade\" code")
return ""
}
authCodeCache =
client.newCall(
GET(
"$ADS_URL/?token=$publicidade",
headers = newHeaders,
),
)
.execute()
.body.string()
.substringAfter("\"publicidade\"")
.substringAfter('"')
.substringBefore('"')
if (authCodeCache.isBlank()) {
Log.e("HinataSoulExtractor", "Failed to fetch auth code")
} else {
Log.d("HinataSoulExtractor", "Auth code fetched successfully")
}
return authCodeCache
}
fun getVideoList(response: Response): List<Video> {
val doc = response.asJsoup()
@ -13,19 +98,29 @@ class HinataSoulExtractor(private val headers: Headers) {
val serverUrl = doc.selectFirst("meta[itemprop=contentURL]")!!
.attr("content")
.replace("cdn1", "cdn3")
val type = serverUrl.split('/').get(3)
val qualities = listOfNotNull("SD", "HD", "FULLHD".takeIf { hasFHD })
val thumbUrl = doc.selectFirst("meta[itemprop=thumbnailUrl]")!!
.attr("content")
val type = serverUrl.split("/").get(3)
val qualities = listOfNotNull("SD", "HD", if (hasFHD) "FULLHD" else null)
val paths = listOf("appsd", "apphd").let {
if (type.endsWith("2")) {
it.map { path -> path + "2" }
} else {
it
}
} + listOfNotNull("appfullhd".takeIf { hasFHD })
} + listOf("appfullhd")
val authCode = getAuthCode(serverUrl, thumbUrl)
return qualities.mapIndexed { index, quality ->
val path = paths[index]
val url = serverUrl.replace(type, path)
val url = serverUrl.replace(type, path) + authCode
Video(url, quality, url, headers = headers)
}.reversed()
}
companion object {
val ADS_URL = "https://ads.anitube.vip"
val SITE_URL = "https://www.anitube.vip"
}
}