fix(pt/hinatasoul): Fix video extractor (#2386)
This commit is contained in:
@ -7,7 +7,7 @@ ext {
|
||||
extName = 'Hinata Soul'
|
||||
pkgNameSuffix = 'pt.hinatasoul'
|
||||
extClass = '.HinataSoul'
|
||||
extVersionCode = 3
|
||||
extVersionCode = 4
|
||||
libVersion = '13'
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,6 @@ import eu.kanade.tachiyomi.animesource.online.ParsedAnimeHttpSource
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.network.asObservableSuccess
|
||||
import eu.kanade.tachiyomi.util.asJsoup
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import org.jsoup.nodes.Document
|
||||
import org.jsoup.nodes.Element
|
||||
@ -45,73 +44,32 @@ class HinataSoul : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
||||
|
||||
// ============================== Popular ===============================
|
||||
override fun popularAnimeSelector() = "div.FsssItem:contains(Mais Vistos) > a"
|
||||
override fun popularAnimeRequest(page: Int): Request = GET(baseUrl)
|
||||
|
||||
override fun popularAnimeRequest(page: Int) = GET(baseUrl)
|
||||
|
||||
override fun popularAnimeFromElement(element: Element) = SAnime.create().apply {
|
||||
setUrlWithoutDomain(element.attr("href"))
|
||||
title = element.text()
|
||||
}
|
||||
override fun popularAnimeNextPageSelector(): String? = null
|
||||
|
||||
// ============================== Episodes ==============================
|
||||
override fun episodeListSelector() = "div.aniContainer a"
|
||||
override fun episodeListParse(response: Response): List<SEpisode> {
|
||||
var doc = getRealDoc(response.asJsoup())
|
||||
val originalUrl = doc.location()
|
||||
var pageNum = 1
|
||||
val totalEpisodes = buildList {
|
||||
do {
|
||||
if (pageNum > 1) {
|
||||
doc = client.newCall(GET(originalUrl + "/page/$pageNum"))
|
||||
.execute()
|
||||
.asJsoup()
|
||||
}
|
||||
doc.select(episodeListSelector()).forEach {
|
||||
add(episodeFromElement(it))
|
||||
}
|
||||
pageNum++
|
||||
} while (hasNextPage(doc))
|
||||
}
|
||||
return totalEpisodes.reversed()
|
||||
}
|
||||
override fun popularAnimeNextPageSelector() = null
|
||||
|
||||
override fun episodeFromElement(element: Element) = SEpisode.create().apply {
|
||||
val title = element.attr("title")
|
||||
// =============================== Latest ===============================
|
||||
override fun latestUpdatesRequest(page: Int) = GET(baseUrl)
|
||||
|
||||
override fun latestUpdatesSelector() =
|
||||
"div.tituloContainer:contains(lançamento) + div.epiContainer a"
|
||||
|
||||
override fun latestUpdatesFromElement(element: Element) = SAnime.create().apply {
|
||||
setUrlWithoutDomain(element.attr("href"))
|
||||
name = title
|
||||
episode_number = title.substringAfterLast(" ").toFloatOrNull() ?: 0F
|
||||
date_upload = element.selectFirst("div.lancaster_episodio_info_data")!!
|
||||
.text()
|
||||
.toDate()
|
||||
val img = element.selectFirst("img")!!
|
||||
thumbnail_url = img.attr("src")
|
||||
title = img.attr("alt")
|
||||
}
|
||||
|
||||
// ============================ Video Links =============================
|
||||
override fun videoListParse(response: Response): List<Video> {
|
||||
return HinataSoulExtractor(headers).getVideoList(response)
|
||||
}
|
||||
|
||||
override fun videoListSelector() = throw Exception("not used")
|
||||
override fun videoFromElement(element: Element) = throw Exception("not used")
|
||||
override fun videoUrlParse(document: Document) = throw Exception("not used")
|
||||
override fun latestUpdatesNextPageSelector() = null
|
||||
|
||||
// =============================== Search ===============================
|
||||
override fun searchAnimeSelector(): String = episodeListSelector()
|
||||
override fun searchAnimeNextPageSelector() = throw Exception("not used")
|
||||
|
||||
override fun searchAnimeFromElement(element: Element) = SAnime.create().apply {
|
||||
setUrlWithoutDomain(element.attr("href"))
|
||||
thumbnail_url = element.selectFirst("img")!!.attr("src")
|
||||
title = element.selectFirst("div.ultimosAnimesHomeItemInfosNome")!!.text()
|
||||
}
|
||||
|
||||
override fun searchAnimeParse(response: Response): AnimesPage {
|
||||
val document = response.asJsoup()
|
||||
val animes = document.select(searchAnimeSelector()).map {
|
||||
searchAnimeFromElement(it)
|
||||
}
|
||||
val hasNext = hasNextPage(document)
|
||||
return AnimesPage(animes, hasNext)
|
||||
}
|
||||
|
||||
override fun fetchSearchAnime(page: Int, query: String, filters: AnimeFilterList): Observable<AnimesPage> {
|
||||
return if (query.startsWith(PREFIX_SEARCH)) {
|
||||
val slug = query.removePrefix(PREFIX_SEARCH)
|
||||
@ -128,9 +86,26 @@ class HinataSoul : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
||||
return AnimesPage(listOf(details), false)
|
||||
}
|
||||
|
||||
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request =
|
||||
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList) =
|
||||
GET("$baseUrl/busca?busca=$query&page=$page")
|
||||
|
||||
override fun searchAnimeSelector() = episodeListSelector()
|
||||
|
||||
override fun searchAnimeNextPageSelector() = null
|
||||
|
||||
override fun searchAnimeFromElement(element: Element) = SAnime.create().apply {
|
||||
setUrlWithoutDomain(element.attr("href"))
|
||||
thumbnail_url = element.selectFirst("img")!!.attr("src")
|
||||
title = element.selectFirst("div.ultimosAnimesHomeItemInfosNome")!!.text()
|
||||
}
|
||||
|
||||
override fun searchAnimeParse(response: Response): AnimesPage {
|
||||
val document = response.use { it.asJsoup() }
|
||||
val animes = document.select(searchAnimeSelector()).map(::searchAnimeFromElement)
|
||||
val hasNext = hasNextPage(document)
|
||||
return AnimesPage(animes, hasNext)
|
||||
}
|
||||
|
||||
// =========================== Anime Details ============================
|
||||
override fun animeDetailsParse(document: Document) = SAnime.create().apply {
|
||||
val doc = getRealDoc(document)
|
||||
@ -157,19 +132,43 @@ class HinataSoul : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
||||
}
|
||||
}
|
||||
|
||||
// =============================== Latest ===============================
|
||||
override fun latestUpdatesNextPageSelector(): String? = null
|
||||
override fun latestUpdatesSelector(): String =
|
||||
"div.tituloContainer:contains(lançamento) + div.epiContainer a"
|
||||
|
||||
override fun latestUpdatesFromElement(element: Element) = SAnime.create().apply {
|
||||
setUrlWithoutDomain(element.attr("href"))
|
||||
val img = element.selectFirst("img")!!
|
||||
thumbnail_url = img.attr("src")
|
||||
title = img.attr("alt")
|
||||
// ============================== Episodes ==============================
|
||||
override fun episodeListSelector() = "div.aniContainer a"
|
||||
override fun episodeListParse(response: Response): List<SEpisode> {
|
||||
var doc = getRealDoc(response.use { it.asJsoup() })
|
||||
val totalEpisodes = buildList {
|
||||
do {
|
||||
if (isNotEmpty()) {
|
||||
val url = doc.selectFirst("div.mwidth > a:containsOwn(»)")!!.absUrl("href")
|
||||
doc = client.newCall(GET(url, headers)).execute().use { it.asJsoup() }
|
||||
}
|
||||
doc.select(episodeListSelector())
|
||||
.map(::episodeFromElement)
|
||||
.let(::addAll)
|
||||
} while (hasNextPage(doc))
|
||||
reverse()
|
||||
}
|
||||
return totalEpisodes
|
||||
}
|
||||
|
||||
override fun latestUpdatesRequest(page: Int): Request = GET(baseUrl)
|
||||
override fun episodeFromElement(element: Element) = SEpisode.create().apply {
|
||||
val title = element.attr("title")
|
||||
setUrlWithoutDomain(element.attr("href"))
|
||||
name = title
|
||||
episode_number = title.substringBeforeLast(" - FINAL").substringAfterLast(" ").toFloatOrNull() ?: 0F
|
||||
date_upload = element.selectFirst("div.lancaster_episodio_info_data")!!
|
||||
.text()
|
||||
.toDate()
|
||||
}
|
||||
|
||||
// ============================ Video Links =============================
|
||||
private val extractor by lazy { HinataSoulExtractor(headers) }
|
||||
|
||||
override fun videoListParse(response: Response) = extractor.getVideoList(response)
|
||||
|
||||
override fun videoListSelector() = throw Exception("not used")
|
||||
override fun videoFromElement(element: Element) = throw Exception("not used")
|
||||
override fun videoUrlParse(document: Document) = throw Exception("not used")
|
||||
|
||||
// ============================== Settings ==============================
|
||||
override fun setupPreferenceScreen(screen: PreferenceScreen) {
|
||||
@ -202,13 +201,18 @@ class HinataSoul : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
||||
val currentUrl = doc.location()
|
||||
val nextUrl = doc.selectFirst("a:contains(»)")!!.attr("href")
|
||||
val endings = listOf("/1", "page=1")
|
||||
return !endings.any(nextUrl::endsWith) && currentUrl != nextUrl
|
||||
return endings.none(nextUrl::endsWith) && currentUrl != nextUrl
|
||||
}
|
||||
|
||||
private val animeMenuSelector = "div.controlesBoxItem > a:has(i.iconLista)"
|
||||
private fun getRealDoc(document: Document): Document {
|
||||
if (!document.location().contains("/videos/")) {
|
||||
return document
|
||||
}
|
||||
|
||||
return document.selectFirst(animeMenuSelector)?.let {
|
||||
client.newCall(GET(it.attr("href"), headers)).execute().asJsoup()
|
||||
client.newCall(GET(it.attr("href"), headers)).execute()
|
||||
.use { r -> r.asJsoup() }
|
||||
} ?: document
|
||||
}
|
||||
|
||||
|
@ -8,22 +8,24 @@ import okhttp3.Response
|
||||
class HinataSoulExtractor(private val headers: Headers) {
|
||||
|
||||
fun getVideoList(response: Response): List<Video> {
|
||||
val html = response.body.string()
|
||||
val doc = response.asJsoup(html)
|
||||
val hasFHD = doc.selectFirst("div.Aba:contains(FULLHD)") != null
|
||||
val regex = Regex("""file: '(\S+?)',""")
|
||||
return regex.findAll(html).mapNotNull {
|
||||
val videoUrl = it.groupValues[1]
|
||||
// prevent some http 404 due to the source returning false-positives
|
||||
if ("appfullhd" in videoUrl && !hasFHD) {
|
||||
null
|
||||
val doc = response.use { it.asJsoup() }
|
||||
val hasFHD = doc.selectFirst("div.abaItem:contains(FULLHD)") != null
|
||||
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 paths = listOf("appsd", "apphd").let {
|
||||
if (type.endsWith("2")) {
|
||||
it.map { path -> path + "2" }
|
||||
} else {
|
||||
val quality = videoUrl.substringAfter("app")
|
||||
.substringBefore("/")
|
||||
.substringBefore("2") // prevents "HD2", "SD2" etc
|
||||
.uppercase()
|
||||
Video(videoUrl, quality, videoUrl, headers = headers)
|
||||
it
|
||||
}
|
||||
}.toList()
|
||||
} + listOfNotNull("appfullhd".takeIf { hasFHD })
|
||||
return qualities.mapIndexed { index, quality ->
|
||||
val path = paths[index]
|
||||
val url = serverUrl.replace(type, path)
|
||||
Video(url, quality, url, headers = headers)
|
||||
}.reversed()
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user