From 1b655e5cd525e28f276f5a68fe0e5c0afc4b481d Mon Sep 17 00:00:00 2001 From: Claudemirovsky <63046606+Claudemirovsky@users.noreply.github.com> Date: Tue, 29 Aug 2023 09:07:03 -0300 Subject: [PATCH] fix(ar/animerco): Update baseUrl + fix episode list (#2096) --- src/ar/animerco/build.gradle | 14 +- .../animeextension/ar/animerco/Animerco.kt | 429 ++++++++---------- .../extractors/MpforuploadExtractor.kt | 19 - .../ar/animerco/extractors/SharedExtractor.kt | 10 +- .../ar/animerco/extractors/UQLoadExtractor.kt | 16 +- .../ar/animerco/extractors/VidBomExtractor.kt | 54 --- 6 files changed, 206 insertions(+), 336 deletions(-) delete mode 100644 src/ar/animerco/src/eu/kanade/tachiyomi/animeextension/ar/animerco/extractors/MpforuploadExtractor.kt delete mode 100644 src/ar/animerco/src/eu/kanade/tachiyomi/animeextension/ar/animerco/extractors/VidBomExtractor.kt diff --git a/src/ar/animerco/build.gradle b/src/ar/animerco/build.gradle index 2f2eb5b33..4c059e2d6 100644 --- a/src/ar/animerco/build.gradle +++ b/src/ar/animerco/build.gradle @@ -1,11 +1,13 @@ -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' +plugins { + alias(libs.plugins.android.application) + alias(libs.plugins.kotlin.android) +} ext { extName = 'Animerco' pkgNameSuffix = 'ar.animerco' extClass = '.Animerco' - extVersionCode = 30 + extVersionCode = 31 libVersion = '13' } @@ -13,7 +15,11 @@ dependencies { implementation(project(':lib-gdriveplayer-extractor')) implementation(project(':lib-streamtape-extractor')) implementation(project(':lib-dood-extractor')) - implementation "dev.datlag.jsunpacker:jsunpacker:1.0.1" + implementation(project(':lib-vidbom-extractor')) + implementation(project(':lib-mp4upload-extractor')) + implementation(project(':lib-streamwish-extractor')) + implementation(project(':lib-yourupload-extractor')) + implementation(project(':lib-okru-extractor')) } apply from: "$rootDir/common.gradle" diff --git a/src/ar/animerco/src/eu/kanade/tachiyomi/animeextension/ar/animerco/Animerco.kt b/src/ar/animerco/src/eu/kanade/tachiyomi/animeextension/ar/animerco/Animerco.kt index 7735c1f56..5d2b33c8d 100644 --- a/src/ar/animerco/src/eu/kanade/tachiyomi/animeextension/ar/animerco/Animerco.kt +++ b/src/ar/animerco/src/eu/kanade/tachiyomi/animeextension/ar/animerco/Animerco.kt @@ -6,7 +6,6 @@ import androidx.preference.ListPreference import androidx.preference.PreferenceScreen import eu.kanade.tachiyomi.animeextension.ar.animerco.extractors.SharedExtractor import eu.kanade.tachiyomi.animeextension.ar.animerco.extractors.UQLoadExtractor -import eu.kanade.tachiyomi.animeextension.ar.animerco.extractors.VidBomExtractor import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource import eu.kanade.tachiyomi.animesource.model.AnimeFilterList import eu.kanade.tachiyomi.animesource.model.SAnime @@ -15,12 +14,20 @@ import eu.kanade.tachiyomi.animesource.model.Video import eu.kanade.tachiyomi.animesource.online.ParsedAnimeHttpSource import eu.kanade.tachiyomi.lib.doodextractor.DoodExtractor import eu.kanade.tachiyomi.lib.gdriveplayerextractor.GdrivePlayerExtractor +import eu.kanade.tachiyomi.lib.mp4uploadextractor.Mp4uploadExtractor +import eu.kanade.tachiyomi.lib.okruextractor.OkruExtractor import eu.kanade.tachiyomi.lib.streamtapeextractor.StreamTapeExtractor +import eu.kanade.tachiyomi.lib.streamwishextractor.StreamWishExtractor +import eu.kanade.tachiyomi.lib.vidbomextractor.VidBomExtractor +import eu.kanade.tachiyomi.lib.youruploadextractor.YourUploadExtractor import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.util.asJsoup +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.async +import kotlinx.coroutines.awaitAll +import kotlinx.coroutines.runBlocking import okhttp3.FormBody -import okhttp3.Headers import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.Response @@ -29,191 +36,186 @@ import org.jsoup.nodes.Element import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get import java.lang.Exception -import java.text.SimpleDateFormat -import java.util.Locale class Animerco : ConfigurableAnimeSource, ParsedAnimeHttpSource() { override val name = "Animerco" - override val baseUrl = "https://animerco.com" + override val baseUrl = "https://animerco.org" override val lang = "ar" override val supportsLatest = false - private val dateFormat: SimpleDateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.US) - override val client: OkHttpClient = network.cloudflareClient private val preferences: SharedPreferences by lazy { Injekt.get().getSharedPreferences("source_$id", 0x0000) } - // Popular Anime + // ============================== Popular =============================== + override fun popularAnimeRequest(page: Int) = GET("$baseUrl/animes/page/$page/") - override fun popularAnimeSelector(): String = "div.media-block" + override fun popularAnimeSelector() = "div.media-block > div > a.image" - override fun popularAnimeRequest(page: Int): Request = GET("$baseUrl/animes/page/$page/") // page/$page - - override fun popularAnimeFromElement(element: Element): SAnime { - val anime = SAnime.create() - anime.setUrlWithoutDomain(element.select("a").attr("href")) - anime.thumbnail_url = element.select("a").attr("data-src") - anime.title = element.select("div.info a h3").text() - return anime + override fun popularAnimeFromElement(element: Element) = SAnime.create().apply { + setUrlWithoutDomain(element.attr("href")) + thumbnail_url = element.attr("data-src") + title = element.attr("title") } - override fun popularAnimeNextPageSelector(): String = "a.ti-arrow-left-c" + override fun popularAnimeNextPageSelector() = "ul.pagination li a:has(i.fa-left-long)" - // Episodes + // =============================== Latest =============================== + override fun latestUpdatesRequest(page: Int): Request = throw Exception("Not used") + override fun latestUpdatesSelector(): String = throw Exception("Not used") + override fun latestUpdatesFromElement(element: Element): SAnime = throw Exception("Not used") + override fun latestUpdatesNextPageSelector(): String = throw Exception("Not used") - override fun episodeListSelector() = throw Exception("not used") + // =============================== Search =============================== + override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList) = GET("$baseUrl/page/$page/?s=$query") + + override fun searchAnimeSelector() = popularAnimeSelector() + override fun searchAnimeFromElement(element: Element) = popularAnimeFromElement(element) + override fun searchAnimeNextPageSelector() = popularAnimeNextPageSelector() + + // =========================== Anime Details ============================ + override fun animeDetailsParse(document: Document) = SAnime.create().apply { + document.selectFirst("a.poster")?.run { + thumbnail_url = attr("data-src") + title = attr("title").ifEmpty { + document.selectFirst("div.media-title h1")!!.text() + } + } + + val infosDiv = document.selectFirst("ul.media-info")!! + author = infosDiv.select("li:contains(الشبكات) a").eachText() + .joinToString() + .takeIf(String::isNotBlank) + artist = infosDiv.select("li:contains(الأستوديو) a").eachText() + .joinToString() + .takeIf(String::isNotBlank) + genre = document.select("nav.Nvgnrs a, ul.media-info li:contains(النوع) a") + .eachText() + .joinToString() + + description = buildString { + document.selectFirst("div.media-story p")?.also { + append(it.text()) + } + document.selectFirst("div.media-title > h3.alt-title")?.also { + append("\n\nAlternative title: " + it.text()) + } + } + + status = document.select("ul.chapters-list a.se-title > span.badge") + .eachText() + .let { items -> + when { + items.all { it.contains("مكتمل") } -> SAnime.COMPLETED + items.any { it.contains("يعرض الأن") } -> SAnime.ONGOING + else -> SAnime.UNKNOWN + } + } + } + + // ============================== Episodes ============================== + override fun episodeListSelector() = "ul.chapters-list li a:has(h3)" override fun episodeListParse(response: Response): List { - val document = response.asJsoup() - val episodeList = mutableListOf() - // val seriesLink1 = document.select("ol[itemscope] li:last-child a").attr("href") - val seriesLink = document.select("link[rel=canonical]").attr("href") - val type = document.select("link[rel=canonical]").attr("href") - if (type.contains("animes")) { - val seasonsHtml = client.newCall( - GET( - seriesLink, - // headers = Headers.headersOf("Referer", document.location()) - ), - ).execute().asJsoup() - val seasonsElements = seasonsHtml.select("ul.chapters-list li a.title") - seasonsElements.reversed().forEach { - val seasonEpList = parseEpisodesFromSeries(it) - episodeList.addAll(seasonEpList) - } - } else { - val movieUrl = seriesLink - val episode = SEpisode.create() - episode.name = document.select("span.alt-title").text() - episode.episode_number = 1F - episode.setUrlWithoutDomain(movieUrl) - episodeList.add(episode) + val document = response.use { it.asJsoup() } + if (document.location().contains("/movies/")) { + return listOf( + SEpisode.create().apply { + setUrlWithoutDomain(document.location()) + episode_number = 1F + name = "Movie" + }, + ) } - return episodeList + + return document.select(episodeListSelector()).flatMap { el -> + val doc = client.newCall(GET(el.attr("abs:href"), headers)).execute() + .use { it.asJsoup() } + val seasonName = doc.selectFirst("div.media-title h1")!!.text() + val seasonNum = seasonName.substringAfterLast(" ").toIntOrNull() ?: 1 + doc.select(episodeListSelector()).map { + episodeFromElement(it, seasonName, seasonNum) + }.reversed() + }.reversed() } - private fun parseEpisodesFromSeries(element: Element): List { - val seasonName = element.text() - val episodesUrl = element.attr("abs:href") - val episodesHtml = client.newCall( - GET( - episodesUrl, - ), - ).execute().asJsoup() - val episodeElements = episodesHtml.select("ul.chapters-list li") - return episodeElements.map { episodeFromElement(it) } + private fun episodeFromElement(element: Element, seasonName: String, seasonNum: Int) = SEpisode.create().apply { + setUrlWithoutDomain(element.attr("href")) + val epText = element.selectFirst("h3")!!.ownText() + name = "$seasonName: " + epText + val epNum = epText.filter(Char::isDigit) + // good luck trying to track this xD + episode_number = "$seasonNum.${epNum.padStart(3, '0')}".toFloatOrNull() ?: 1F } - override fun episodeFromElement(element: Element): SEpisode { - val episode = SEpisode.create() - val epNum = getNumberFromEpsString(element.select("a.title h3").text()) - episode.episode_number = when { - (epNum.isNotEmpty()) -> epNum.toFloat() - else -> 1F - } - // element.select("td > span.Num").text().toFloat() - // val SeasonNum = element.ownerDocument()!!.select("div.Title span").text() - val seasonName = element.ownerDocument()!!.select("div.media-title h1").text() - episode.name = "$seasonName : " + element.select("a.title h3").text() - episode.setUrlWithoutDomain(element.select("a.title").attr("abs:href")) - return episode - } - - private fun getNumberFromEpsString(epsStr: String): String { - return epsStr.filter { it.isDigit() } - } - - // Video urls + override fun episodeFromElement(element: Element) = throw Exception("not used") + // ============================ Video Links ============================= override fun videoListParse(response: Response): List