diff --git a/src/pt/megaflix/build.gradle b/src/pt/megaflix/build.gradle index 3e06bd88c..90b0c355e 100644 --- a/src/pt/megaflix/build.gradle +++ b/src/pt/megaflix/build.gradle @@ -7,7 +7,7 @@ ext { extName = 'Megaflix' pkgNameSuffix = 'pt.megaflix' extClass = '.Megaflix' - extVersionCode = 6 + extVersionCode = 7 libVersion = '13' containsNsfw = true } @@ -15,6 +15,7 @@ ext { dependencies { implementation(project(':lib-mixdrop-extractor')) implementation(project(":lib-streamtape-extractor")) + implementation(project(":lib-playlist-utils")) // for mixdrop and megaflix implementation("dev.datlag.jsunpacker:jsunpacker:1.0.1") } diff --git a/src/pt/megaflix/src/eu/kanade/tachiyomi/animeextension/pt/megaflix/Megaflix.kt b/src/pt/megaflix/src/eu/kanade/tachiyomi/animeextension/pt/megaflix/Megaflix.kt index 2b77d59ad..315e35eaa 100644 --- a/src/pt/megaflix/src/eu/kanade/tachiyomi/animeextension/pt/megaflix/Megaflix.kt +++ b/src/pt/megaflix/src/eu/kanade/tachiyomi/animeextension/pt/megaflix/Megaflix.kt @@ -40,36 +40,93 @@ class Megaflix : ConfigurableAnimeSource, ParsedAnimeHttpSource() { override val supportsLatest = true + override fun headersBuilder() = super.headersBuilder().add("Referer", "$baseUrl/") + private val preferences: SharedPreferences by lazy { Injekt.get().getSharedPreferences("source_$id", 0x0000) } // ============================== Popular =============================== - override fun popularAnimeFromElement(element: Element): SAnime { - return SAnime.create().apply { - title = element.selectFirst("h2.entry-title")!!.text() - setUrlWithoutDomain(element.selectFirst("a.lnk-blk")!!.attr("href")) - thumbnail_url = "https:" + element.selectFirst("img")!!.attr("src") - } + override fun popularAnimeRequest(page: Int) = GET(baseUrl) + + override fun popularAnimeSelector() = "section#widget_list_movies_series-5 li > article" + + override fun popularAnimeFromElement(element: Element) = SAnime.create().apply { + title = element.selectFirst("h2.entry-title")!!.text() + setUrlWithoutDomain(element.selectFirst("a.lnk-blk")!!.attr("href")) + thumbnail_url = element.selectFirst("img")!!.attr("abs:src") } override fun popularAnimeNextPageSelector() = null - override fun popularAnimeRequest(page: Int) = GET(baseUrl) + // =============================== Latest =============================== + override fun latestUpdatesRequest(page: Int): Request { + val pageType = preferences.getString(PREF_LATEST_PAGE_KEY, PREF_LATEST_PAGE_DEFAULT)!! + return GET("$baseUrl/$pageType/page/$page") + } - override fun popularAnimeSelector() = "section#widget_list_movies_series-5 li > article" + override fun latestUpdatesSelector() = "li > article" + + override fun latestUpdatesFromElement(element: Element) = popularAnimeFromElement(element) + + override fun latestUpdatesNextPageSelector() = "div.nav-links > a:containsOwn(PRÓXIMO)" + + // =============================== Search =============================== + override fun fetchSearchAnime(page: Int, query: String, filters: AnimeFilterList): Observable { + return if (query.startsWith(PREFIX_SEARCH)) { // URL intent handler + val path = query.removePrefix(PREFIX_SEARCH) + client.newCall(GET("$baseUrl/$path")) + .asObservableSuccess() + .map(::searchAnimeByPathParse) + } else { + super.fetchSearchAnime(page, query, filters) + } + } + + private fun searchAnimeByPathParse(response: Response): AnimesPage { + val details = animeDetailsParse(response.asJsoup()) + return AnimesPage(listOf(details), false) + } + + override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request { + return if (query.isNotBlank()) { + GET("$baseUrl/page/$page/?s=$query") + } else { + val genre = MegaflixFilters.getGenre(filters) + GET("$baseUrl/categoria/$genre/page/$page") + } + } + + override fun searchAnimeSelector() = latestUpdatesSelector() + + override fun searchAnimeFromElement(element: Element) = popularAnimeFromElement(element) + + override fun searchAnimeNextPageSelector() = latestUpdatesNextPageSelector() + + override fun getFilterList() = MegaflixFilters.FILTER_LIST + + // =========================== Anime Details ============================ + override fun animeDetailsParse(document: Document) = SAnime.create().apply { + setUrlWithoutDomain(document.location()) + val infos = document.selectFirst("div.bd > article.post.single")!! + title = infos.selectFirst("h1.entry-title")!!.text() + thumbnail_url = "https:" + infos.selectFirst("img")!!.attr("src") + genre = infos.select("span.genres > a").eachText().joinToString() + description = infos.selectFirst("div.description")?.text() + } // ============================== Episodes ============================== override fun episodeListSelector() = "li > article.episodes" private fun seasonListSelector() = "section.episodes div.choose-season > a" override fun episodeListParse(response: Response): List { - val seasons = response.asJsoup().select(seasonListSelector()) + val doc = response.use { it.asJsoup() } + val seasons = doc.select(seasonListSelector()) return when { seasons.isEmpty() -> listOf( SEpisode.create().apply { name = "Filme" - setUrlWithoutDomain(response.request.url.toString()) + setUrlWithoutDomain(doc.location()) episode_number = 1F }, ) @@ -79,38 +136,25 @@ class Megaflix : ConfigurableAnimeSource, ParsedAnimeHttpSource() { private fun episodesFromSeason(seasonElement: Element): List { return seasonElement.attr("href").let { url -> - client.newCall(GET(url)).execute() - .asJsoup() + client.newCall(GET(url, headers)).execute() + .use { it.asJsoup() } .select(episodeListSelector()) .map(::episodeFromElement) } } - override fun episodeFromElement(element: Element): SEpisode { - return SEpisode.create().apply { - name = element.selectFirst("h2.entry-title")!!.text() - setUrlWithoutDomain(element.selectFirst("a.lnk-blk")!!.attr("href")) - episode_number = element.selectFirst("span.num-epi") - ?.text() - ?.split("x") - ?.let { - val season = it.first().toFloatOrNull() ?: 0F - val episode = it.last().toFloatOrNull() ?: 0F - (season * 100F) + episode - } - ?: 0F - } - } - - // =========================== Anime Details ============================ - override fun animeDetailsParse(document: Document): SAnime { - return SAnime.create().apply { - val infos = document.selectFirst("div.bd > article.post.single")!! - title = infos.selectFirst("h1.entry-title")!!.text() - thumbnail_url = "https:" + infos.selectFirst("img")!!.attr("src") - genre = infos.select("span.genres > a").eachText().joinToString() - description = infos.selectFirst("div.description")?.text() - } + override fun episodeFromElement(element: Element) = SEpisode.create().apply { + name = element.selectFirst("h2.entry-title")!!.text() + setUrlWithoutDomain(element.selectFirst("a.lnk-blk")!!.attr("href")) + episode_number = element.selectFirst("span.num-epi") + ?.text() + ?.split("x") + ?.let { + val season = it.first().toFloatOrNull() ?: 0F + val episode = it.last().toFloatOrNull() ?: 0F + (season * 100F) + episode + } + ?: 0F } // ============================ Video Links ============================= @@ -125,20 +169,22 @@ class Megaflix : ConfigurableAnimeSource, ParsedAnimeHttpSource() { ?.attr("href") ?.substringAfter("token=") ?.let { Base64.decode(it, Base64.DEFAULT).let(::String) } - ?: return@parallelMap null + ?.substringAfter("||") + ?: return@parallelMap emptyList() - runCatching { getVideoList(url, language) }.getOrNull() - }.filterNotNull().flatten() + runCatching { getVideoList(url, language) }.getOrNull() ?: emptyList() + }.flatten() } + private val mixdropExtractor by lazy { MixDropExtractor(client) } + private val streamtapeExtractor by lazy { StreamTapeExtractor(client) } + private val megaflixExtractor by lazy { MegaflixExtractor(client, headers) } + private fun getVideoList(url: String, language: String): List