AnimesHouse: Show all video qualities + small refactor (#1315)

* fix(Mp4DooExtractor): Show all video qualities

* refactor: General refactoration and preparation to jsoup 1.15.3

* chore: Bump version
This commit is contained in:
Claudemirovsky
2023-02-22 09:10:49 -03:00
committed by GitHub
parent 6572205982
commit 5eb38a3171
5 changed files with 81 additions and 79 deletions

View File

@ -5,7 +5,7 @@ ext {
extName = 'Animes House' extName = 'Animes House'
pkgNameSuffix = 'pt.animeshouse' pkgNameSuffix = 'pt.animeshouse'
extClass = '.AnimesHouse' extClass = '.AnimesHouse'
extVersionCode = 2 extVersionCode = 3
libVersion = '13' libVersion = '13'
} }

View File

@ -58,52 +58,43 @@ class AnimesHouse : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
// ============================== Popular =============================== // ============================== Popular ===============================
override fun popularAnimeSelector(): String = "div#featured-titles div.poster" override fun popularAnimeSelector(): String = "div#featured-titles div.poster"
override fun popularAnimeRequest(page: Int): Request = GET(baseUrl, headers) override fun popularAnimeRequest(page: Int): Request = GET(baseUrl)
override fun popularAnimeFromElement(element: Element): SAnime { override fun popularAnimeFromElement(element: Element): SAnime {
val anime = SAnime.create() return SAnime.create().apply {
val img = element.selectFirst("img") val img = element.selectFirst("img")!!
anime.setUrlWithoutDomain(element.selectFirst("a").attr("href")) setUrlWithoutDomain(element.selectFirst("a")!!.attr("href"))
anime.title = img.attr("alt") title = img.attr("alt")
anime.thumbnail_url = img.attr("src") thumbnail_url = img.attr("src")
return anime
}
override fun popularAnimeNextPageSelector() = throw Exception("not used")
override fun popularAnimeParse(response: Response): AnimesPage {
val document = response.asJsoup()
val animes = document.select(popularAnimeSelector()).map { element ->
popularAnimeFromElement(element)
} }
return AnimesPage(animes, false)
} }
override fun popularAnimeNextPageSelector() = null
// ============================== Episodes ============================== // ============================== Episodes ==============================
override fun episodeListSelector(): String = "ul.episodios > li" override fun episodeListSelector(): String = "ul.episodios > li"
override fun episodeListParse(response: Response): List<SEpisode> { override fun episodeListParse(response: Response): List<SEpisode> {
val doc = getRealDoc(response.asJsoup()) val doc = getRealDoc(response.asJsoup())
val epList = doc.select(episodeListSelector()) val epList = doc.select(episodeListSelector())
if (epList.size < 1) { return if (epList.size < 1) {
val episode = SEpisode.create() SEpisode.create().apply {
episode.setUrlWithoutDomain(response.request.url.toString()) setUrlWithoutDomain(doc.location())
episode.episode_number = 1F episode_number = 1F
episode.name = "Filme" name = "Filme"
return listOf(episode) }.let(::listOf)
} else {
epList.reversed().map { episodeFromElement(it) }
} }
return epList.reversed().map { episodeFromElement(it) }
} }
override fun episodeFromElement(element: Element): SEpisode { override fun episodeFromElement(element: Element): SEpisode {
val episode = SEpisode.create() return SEpisode.create().apply {
val origName = element.selectFirst("div.numerando").text() val origName = element.selectFirst("div.numerando")!!.text()
episode_number = origName.substringAfter("- ").toFloat() + if ("Dub" in origName) 0.5F else 0F
episode.episode_number = origName.substring(origName.indexOf("-") + 1) name = "Temp " + origName.replace(" - ", ": Ep ")
.toFloat() + if ("Dub" in origName) 0.5F else 0F setUrlWithoutDomain(element.selectFirst("a")!!.attr("href"))
episode.name = "Temp " + origName.replace(" - ", ": Ep ") }
episode.setUrlWithoutDomain(element.selectFirst("a").attr("href"))
return episode
} }
// ============================ Video Links ============================= // ============================ Video Links =============================
@ -119,7 +110,7 @@ class AnimesHouse : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
) )
.execute() .execute()
.asJsoup() .asJsoup()
val iframe = doc.selectFirst("iframe") val iframe = doc.selectFirst("iframe")!!
return iframe.attr("src").let { return iframe.attr("src").let {
if (it.startsWith("/redplay")) if (it.startsWith("/redplay"))
RedplayBypasser(client, headers).fromUrl(baseUrl + it) RedplayBypasser(client, headers).fromUrl(baseUrl + it)
@ -130,14 +121,10 @@ class AnimesHouse : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override fun videoListParse(response: Response): List<Video> { override fun videoListParse(response: Response): List<Video> {
val document = response.asJsoup() val document = response.asJsoup()
val players = document.select("ul#playeroptionsul li") val players = document.select("ul#playeroptionsul li")
val videoList = mutableListOf<Video>() return players.flatMap { player ->
players.forEach { player ->
val url = getPlayerUrl(player) val url = getPlayerUrl(player)
val videos = runCatching { getPlayerVideos(url) } runCatching { getPlayerVideos(url) }.getOrDefault(emptyList<Video>())
.getOrNull() ?: emptyList<Video>()
videoList.addAll(videos)
} }
return videoList
} }
private fun getPlayerVideos(url: String): List<Video> { private fun getPlayerVideos(url: String): List<Video> {
@ -152,7 +139,7 @@ class AnimesHouse : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
"edifier" in url -> "edifier" in url ->
EdifierExtractor(client, headers).getVideoList(url) EdifierExtractor(client, headers).getVideoList(url)
"mp4doo" in url -> "mp4doo" in url ->
MpFourDooExtractor(headers).getVideoList(unpackedBody) MpFourDooExtractor(client, headers).getVideoList(unpackedBody)
"clp-new" in url || "gcloud" in url -> "clp-new" in url || "gcloud" in url ->
GenericExtractor(client, headers).getVideoList(url, unpackedBody) GenericExtractor(client, headers).getVideoList(url, unpackedBody)
"mcp_comm" in unpackedBody -> "mcp_comm" in unpackedBody ->
@ -173,9 +160,8 @@ class AnimesHouse : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
} }
override fun searchAnimeParse(response: Response): AnimesPage { override fun searchAnimeParse(response: Response): AnimesPage {
val url = response.request.url.toString()
val document = response.asJsoup() val document = response.asJsoup()
val url = document.location()
val animes = when { val animes = when {
"/generos/" in url -> { "/generos/" in url -> {
document.select(latestUpdatesSelector()).map { element -> document.select(latestUpdatesSelector()).map { element ->
@ -228,10 +214,10 @@ class AnimesHouse : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
} }
override fun searchAnimeFromElement(element: Element): SAnime { override fun searchAnimeFromElement(element: Element): SAnime {
val anime = SAnime.create() return SAnime.create().apply {
anime.setUrlWithoutDomain(element.attr("href")) setUrlWithoutDomain(element.attr("href"))
anime.title = element.text() title = element.text()
return anime }
} }
override fun searchAnimeNextPageSelector(): String = latestUpdatesNextPageSelector() override fun searchAnimeNextPageSelector(): String = latestUpdatesNextPageSelector()
@ -240,20 +226,21 @@ class AnimesHouse : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
// =========================== Anime Details ============================ // =========================== Anime Details ============================
override fun animeDetailsParse(document: Document): SAnime { override fun animeDetailsParse(document: Document): SAnime {
val anime = SAnime.create()
val doc = getRealDoc(document) val doc = getRealDoc(document)
val sheader = doc.selectFirst("div.sheader") val sheader = doc.selectFirst("div.sheader")!!
anime.thumbnail_url = sheader.selectFirst("div.poster > img").attr("src") val anime = SAnime.create()
anime.title = sheader.selectFirst("div.data > h1").text() anime.thumbnail_url = sheader.selectFirst("div.poster > img")!!.attr("src")
anime.title = sheader.selectFirst("div.data > h1")!!.text()
anime.genre = sheader.select("div.data > div.sgeneros > a") anime.genre = sheader.select("div.data > div.sgeneros > a")
.joinToString(", ") { it.text() } .joinToString(", ") { it.text() }
val info = doc.selectFirst("div#info") doc.selectFirst("div#info")?.let { info ->
var description = info.selectFirst("p")?.let { it.text() + "\n" } ?: "" var description = info.selectFirst("p")?.let { it.text() + "\n" } ?: ""
info.getInfo("Título")?.let { description += "$it" } info.getInfo("Título")?.let { description += "$it" }
info.getInfo("Ano")?.let { description += "$it" } info.getInfo("Ano")?.let { description += "$it" }
info.getInfo("Temporadas")?.let { description += "$it" } info.getInfo("Temporadas")?.let { description += "$it" }
info.getInfo("Episódios")?.let { description += "$it" } info.getInfo("Episódios")?.let { description += "$it" }
anime.description = description anime.description = description
}
return anime return anime
} }
@ -265,7 +252,7 @@ class AnimesHouse : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override fun latestUpdatesFromElement(element: Element) = popularAnimeFromElement(element) override fun latestUpdatesFromElement(element: Element) = popularAnimeFromElement(element)
override fun latestUpdatesRequest(page: Int): Request = GET("$baseUrl/episodio/page/$page", headers) override fun latestUpdatesRequest(page: Int): Request = GET("$baseUrl/episodio/page/$page", headers)
// ============================== Settings ============================== // ============================== Settings ==============================
override fun setupPreferenceScreen(screen: PreferenceScreen) { override fun setupPreferenceScreen(screen: PreferenceScreen) {
val videoQualityPref = ListPreference(screen.context).apply { val videoQualityPref = ListPreference(screen.context).apply {
key = AHConstants.PREFERRED_QUALITY key = AHConstants.PREFERRED_QUALITY
@ -291,7 +278,7 @@ class AnimesHouse : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
private fun getRealDoc(document: Document): Document { private fun getRealDoc(document: Document): Document {
val menu = document.selectFirst(animeMenuSelector) val menu = document.selectFirst(animeMenuSelector)
if (menu != null) { if (menu != null) {
val originalUrl = menu.parent().attr("href") val originalUrl = menu.parent()!!.attr("href")
val req = client.newCall(GET(originalUrl, headers)).execute() val req = client.newCall(GET(originalUrl, headers)).execute()
return req.asJsoup() return req.asJsoup()
} else { } else {
@ -302,23 +289,15 @@ class AnimesHouse : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
private fun Element.getInfo(substring: String): String? { private fun Element.getInfo(substring: String): String? {
val target = this.selectFirst("div.custom_fields:contains($substring)") val target = this.selectFirst("div.custom_fields:contains($substring)")
?: return null ?: return null
val key = target.selectFirst("b").text() val key = target.selectFirst("b")!!.text()
val value = target.selectFirst("span").text() val value = target.selectFirst("span")!!.text()
return "\n$key: $value" return "\n$key: $value"
} }
override fun List<Video>.sort(): List<Video> { override fun List<Video>.sort(): List<Video> {
val quality = preferences.getString(AHConstants.PREFERRED_QUALITY, AHConstants.DEFAULT_QUALITY)!! val quality = preferences.getString(AHConstants.PREFERRED_QUALITY, AHConstants.DEFAULT_QUALITY)!!
val newList = mutableListOf<Video>() return sortedWith(
var preferred = 0 compareBy { it.quality.contains(quality) }
for (video in this) { ).reversed()
if (quality in video.quality) {
newList.add(preferred, video)
preferred++
} else {
newList.add(video)
}
}
return newList
} }
} }

View File

@ -1,6 +1,5 @@
package eu.kanade.tachiyomi.animeextension.pt.animeshouse.extractors package eu.kanade.tachiyomi.animeextension.pt.animeshouse.extractors
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
import okhttp3.Headers import okhttp3.Headers
@ -20,7 +19,6 @@ class GenericExtractor(
"gcloud" in url -> Pair("GCLOUD", REGEX_GCLOUD_PLAYER) "gcloud" in url -> Pair("GCLOUD", REGEX_GCLOUD_PLAYER)
else -> Pair("CLP", REGEX_CLP_PLAYER) else -> Pair("CLP", REGEX_CLP_PLAYER)
} }
Log.e(player, url)
val playlistUrl = regex.find(js)!!.groupValues.get(1) val playlistUrl = regex.find(js)!!.groupValues.get(1)
if ("m3u8.php" in playlistUrl) { if ("m3u8.php" in playlistUrl) {

View File

@ -1,10 +1,14 @@
package eu.kanade.tachiyomi.animeextension.pt.animeshouse.extractors package eu.kanade.tachiyomi.animeextension.pt.animeshouse.extractors
import eu.kanade.tachiyomi.animesource.model.Video import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.network.GET
import okhttp3.Headers import okhttp3.Headers
import okhttp3.OkHttpClient
class MpFourDooExtractor(private val headers: Headers) { class MpFourDooExtractor(
private val client: OkHttpClient,
private val headers: Headers
) {
private val REGEX_MPDOO = Regex("file\":\"(.*?)\"") private val REGEX_MPDOO = Regex("file\":\"(.*?)\"")
private val PLAYER_NAME = "Mp4Doo" private val PLAYER_NAME = "Mp4Doo"
@ -12,6 +16,27 @@ class MpFourDooExtractor(private val headers: Headers) {
val videoUrl = REGEX_MPDOO.find(js)!!.groupValues val videoUrl = REGEX_MPDOO.find(js)!!.groupValues
.get(1) .get(1)
.replace("fy..", "fy.v.") .replace("fy..", "fy.v.")
return listOf(Video(videoUrl, PLAYER_NAME, videoUrl, headers)) return if (videoUrl.endsWith("playlist.m3u8")) {
val playlistBody = client.newCall(GET(videoUrl, headers)).execute()
.body!!.string()
val separator = "#EXT-X-STREAM-INF:"
playlistBody.substringAfter(separator).split(separator).map {
val quality = PLAYER_NAME + " - " + it.substringAfter("RESOLUTION=")
.substringAfter("x")
.substringBefore("\n")
.substringBefore(",") + "p"
val path = it.substringAfter("\n").substringBefore("\n")
val playlistUrl = if (!path.startsWith("https:")) {
videoUrl.replace("playlist.m3u8", path)
} else path
Video(playlistUrl, quality, playlistUrl, headers)
}
} else {
listOf(Video(videoUrl, PLAYER_NAME, videoUrl, headers))
}
} }
} }

View File

@ -15,7 +15,7 @@ class RedplayBypasser(
fun fromUrl(url: String): String { fun fromUrl(url: String): String {
val firstDoc = client.newCall(GET(url, headers)).execute().asJsoup() val firstDoc = client.newCall(GET(url, headers)).execute().asJsoup()
val next = firstDoc.selectFirst("a").attr("href") val next = firstDoc.selectFirst("a")!!.attr("href")
var nextPage = client.newCall(GET(next, headers)).execute() var nextPage = client.newCall(GET(next, headers)).execute()
var iframeUrl = "" var iframeUrl = ""
var formUrl = next var formUrl = next
@ -29,7 +29,7 @@ class RedplayBypasser(
.set("Referer", formUrl) .set("Referer", formUrl)
.build() .build()
val formBody = FormBody.Builder() val formBody = FormBody.Builder()
formUrl = nextDoc.selectFirst("form").attr("action") formUrl = nextDoc.selectFirst("form")!!.attr("action")
nextDoc.select("input[name]").forEach { nextDoc.select("input[name]").forEach {
formBody.add(it.attr("name"), it.attr("value")) formBody.add(it.attr("name"), it.attr("value"))
} }