From 440917b8ffede862558c0c61321c0187d4e4f79e Mon Sep 17 00:00:00 2001 From: imper1aldev <23511335+imper1aldev@users.noreply.github.com> Date: Mon, 9 Dec 2024 15:48:19 -0600 Subject: [PATCH] Fix(src/es): Pelisforte, HomeCine and MetroSeries fixes (#419) * MetroSeries fixes Closes #370 * HomeCine fixes Closes #384 * PelisForte fixes Closes #411 --- src/es/homecine/build.gradle | 9 +- .../animeextension/es/homecine/HomeCine.kt | 266 +++++++++++------- src/es/metroseries/build.gradle | 2 +- .../es/metroseries/MetroSeries.kt | 212 ++++++-------- src/es/pelisforte/build.gradle | 3 +- .../es/pelisforte/PelisForte.kt | 136 +++++---- 6 files changed, 311 insertions(+), 317 deletions(-) diff --git a/src/es/homecine/build.gradle b/src/es/homecine/build.gradle index d29ecf88..a12f28fa 100644 --- a/src/es/homecine/build.gradle +++ b/src/es/homecine/build.gradle @@ -1,11 +1,18 @@ ext { extName = 'HomeCine' extClass = '.HomeCine' - extVersionCode = 1 + extVersionCode = 2 } apply from: "$rootDir/common.gradle" dependencies { + implementation(project(':lib:burstcloud-extractor')) + implementation(project(':lib:mp4upload-extractor')) + implementation(project(':lib:streamwish-extractor')) + implementation(project(':lib:voe-extractor')) + implementation(project(':lib:yourupload-extractor')) implementation(project(':lib:fastream-extractor')) + implementation(project(':lib:upstream-extractor')) + implementation(project(':lib:filemoon-extractor')) } \ No newline at end of file diff --git a/src/es/homecine/src/eu/kanade/tachiyomi/animeextension/es/homecine/HomeCine.kt b/src/es/homecine/src/eu/kanade/tachiyomi/animeextension/es/homecine/HomeCine.kt index a9ff7965..187b50ef 100644 --- a/src/es/homecine/src/eu/kanade/tachiyomi/animeextension/es/homecine/HomeCine.kt +++ b/src/es/homecine/src/eu/kanade/tachiyomi/animeextension/es/homecine/HomeCine.kt @@ -5,19 +5,27 @@ import android.content.SharedPreferences import androidx.preference.ListPreference import androidx.preference.PreferenceScreen import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource -import eu.kanade.tachiyomi.animesource.model.AnimeFilter import eu.kanade.tachiyomi.animesource.model.AnimeFilterList import eu.kanade.tachiyomi.animesource.model.AnimesPage import eu.kanade.tachiyomi.animesource.model.SAnime import eu.kanade.tachiyomi.animesource.model.SEpisode import eu.kanade.tachiyomi.animesource.model.Video import eu.kanade.tachiyomi.animesource.online.AnimeHttpSource +import eu.kanade.tachiyomi.lib.burstcloudextractor.BurstCloudExtractor import eu.kanade.tachiyomi.lib.fastreamextractor.FastreamExtractor +import eu.kanade.tachiyomi.lib.filemoonextractor.FilemoonExtractor +import eu.kanade.tachiyomi.lib.mp4uploadextractor.Mp4uploadExtractor +import eu.kanade.tachiyomi.lib.streamwishextractor.StreamWishExtractor +import eu.kanade.tachiyomi.lib.upstreamextractor.UpstreamExtractor +import eu.kanade.tachiyomi.lib.voeextractor.VoeExtractor +import eu.kanade.tachiyomi.lib.youruploadextractor.YourUploadExtractor import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.parallelCatchingFlatMapBlocking +import okhttp3.FormBody import okhttp3.Request import okhttp3.Response +import org.jsoup.nodes.Element import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get @@ -25,7 +33,7 @@ class HomeCine : ConfigurableAnimeSource, AnimeHttpSource() { override val name = "HomeCine" - override val baseUrl = "https://www3.homecine.tv" + override val baseUrl = "https://homecine.cc" override val lang = "es" @@ -45,116 +53,175 @@ class HomeCine : ConfigurableAnimeSource, AnimeHttpSource() { private val QUALITY_LIST = arrayOf("1080", "720", "480", "360") private const val PREF_SERVER_KEY = "preferred_server" - private const val PREF_SERVER_DEFAULT = "Fastream" - private val SERVER_LIST = arrayOf("Fastream") + private const val PREF_SERVER_DEFAULT = "YourUpload" + private val SERVER_LIST = arrayOf( + "YourUpload", + "BurstCloud", + "Voe", + "StreamWish", + "Mp4Upload", + "Fastream", + "Upstream", + "Filemoon", + ) } - override fun animeDetailsParse(response: Response): SAnime { - val document = response.asJsoup() - val animeDetails = SAnime.create().apply { - title = document.selectFirst(".mvic-desc h1")?.text()?.trim() ?: "" - status = if (document.location().contains("/series/")) SAnime.UNKNOWN else SAnime.COMPLETED - description = document.selectFirst(".mvic-desc .f-desc")?.ownText() - genre = document.select(".mvic-info [rel='category tag']").joinToString { it.text() } - thumbnail_url = document.selectFirst("[itemprop=image]")?.attr("abs:src")?.replace("/w185/", "/w500/") - document.select(".mvici-left p").map { it.text() }.map { textContent -> - when { - "Director" in textContent -> author = textContent.substringAfter("Director:").trim().split(", ").firstOrNull() - "Actors" in textContent -> artist = textContent.substringAfter("Actors:").trim().split(", ").firstOrNull() - } - } - } - return animeDetails - } - - override fun popularAnimeRequest(page: Int) = GET("$baseUrl/peliculas/page/$page", headers) + override fun popularAnimeRequest(page: Int) = GET("$baseUrl/cartelera-series/page/$page", headers) override fun popularAnimeParse(response: Response): AnimesPage { val document = response.asJsoup() - val elements = document.select("[data-movie-id] > a") - val nextPage = document.select(".pagination li a:contains(Last)").any() + val elements = document.select(".post") + val nextPage = document.select(".nav-links .current ~ a").any() val animeList = elements.map { element -> SAnime.create().apply { - title = element.selectFirst(".mli-info")?.text()?.trim() ?: "" - thumbnail_url = element.selectFirst("img")!!.attr("abs:data-original") - setUrlWithoutDomain(element.attr("abs:href")) + setUrlWithoutDomain(element.selectFirst(".lnk-blk")?.attr("abs:href") ?: "") + title = element.selectFirst(".entry-header .entry-title")?.text() ?: "" + description = element.select(".entry-content p").text() ?: "" + thumbnail_url = element.selectFirst(".post-thumbnail figure img")?.let { getImageUrl(it) } } } return AnimesPage(animeList, nextPage) } - override fun latestUpdatesParse(response: Response) = popularAnimeParse(response) - override fun latestUpdatesRequest(page: Int) = popularAnimeRequest(page) - override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request { - val filterList = if (filters.isEmpty()) getFilterList() else filters - val genreFilter = filterList.find { it is GenreFilter } as GenreFilter + override fun latestUpdatesParse(response: Response) = popularAnimeParse(response) - return when { - query.isNotBlank() -> GET("$baseUrl/page/$page/?s=$query", headers) - genreFilter.state != 0 -> GET("$baseUrl/${genreFilter.toUriPart()}/page/$page", headers) - else -> popularAnimeRequest(page) - } - } + override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList) = GET("$baseUrl/?s=$query", headers) override fun searchAnimeParse(response: Response) = popularAnimeParse(response) + override fun animeDetailsParse(response: Response): SAnime { + val document = response.asJsoup() + return SAnime.create().apply { + title = document.selectFirst("aside .entry-header .entry-title")?.text() ?: "" + description = document.select("aside .description p:not([class])").joinToString { it.text() } + thumbnail_url = document.selectFirst(".post-thumbnail img")?.let { getImageUrl(it)?.replace("/w185/", "/w500/") } + genre = document.select(".genres a").joinToString { it.text() } + status = if (document.location().contains("pelicula")) SAnime.COMPLETED else SAnime.UNKNOWN + } + } + + private fun getImageUrl(element: Element): String? { + return when { + element.hasAttr("data-src") -> element.attr("abs:data-src") + element.hasAttr("src") -> element.attr("abs:src") + else -> null + } + } + override fun episodeListParse(response: Response): List { val document = response.asJsoup() - return if (document.location().contains("/series/")) { - var episodeCounter = 1F - document.select(".tvseason").flatMap { season -> - val noSeason = season.select(".les-title strong").text().substringAfter("Temporada").trim() - season.select(".les-content a").map { ep -> - SEpisode.create().apply { - episode_number = episodeCounter++ - name = "T$noSeason - ${ep.text().trim()}" - setUrlWithoutDomain(ep.attr("abs:href")) - } - } - }.reversed() - } else { + val referer = response.request.url.toString() + return if (referer.contains("pelicula")) { listOf( SEpisode.create().apply { episode_number = 1f - name = "PELÍCULA" - setUrlWithoutDomain(document.location()) + name = "Película" + setUrlWithoutDomain(referer) }, ) + } else { + val chunkSize = Runtime.getRuntime().availableProcessors() + document.select(".sel-temp a") + .sortedByDescending { it.attr("data-season") } + .chunked(chunkSize).flatMap { chunk -> + chunk.parallelCatchingFlatMapBlocking { season -> + getDetailSeason(season, referer) + } + }.sortedByDescending { + it.name.substringBeforeLast("-") + } + } + } + + private fun getDetailSeason(element: Element, referer: String): List { + return try { + val post = element.attr("data-post") + val season = element.attr("data-season") + val formBody = FormBody.Builder() + .add("action", "action_select_season") + .add("season", season) + .add("post", post) + .build() + + val request = Request.Builder() + .url("$baseUrl/wp-admin/admin-ajax.php") + .post(formBody) + .header("Origin", baseUrl) + .header("Referer", referer) + .header("Content-Type", "application/x-www-form-urlencoded") + .build() + val detail = client.newCall(request).execute().asJsoup() + + detail.select(".post").reversed().mapIndexed { idx, it -> + val epNumber = try { + it.select(".entry-header .num-epi").text().substringAfter("x").substringBefore("–").trim() + } catch (_: Exception) { "${idx + 1}" } + + SEpisode.create().apply { + setUrlWithoutDomain(it.select("a").attr("abs:href")) + name = "T$season - Episodio $epNumber" + episode_number = epNumber.toFloat() + } + } + } catch (_: Exception) { + emptyList() } } override fun videoListParse(response: Response): List