From 3818237ce6370abfef2485999a19cc84d37def54 Mon Sep 17 00:00:00 2001 From: Claudemirovsky <63046606+Claudemirovsky@users.noreply.github.com> Date: Sun, 19 Nov 2023 01:44:05 -0300 Subject: [PATCH 1/5] refactor: General refactoration --- src/id/kuramanime/build.gradle | 6 +- .../id/kuramanime/Kuramanime.kt | 175 +++++++++--------- 2 files changed, 94 insertions(+), 87 deletions(-) diff --git a/src/id/kuramanime/build.gradle b/src/id/kuramanime/build.gradle index cfa5ea49c2..fcaab4e8a2 100644 --- a/src/id/kuramanime/build.gradle +++ b/src/id/kuramanime/build.gradle @@ -1,5 +1,7 @@ -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' +plugins { + alias(libs.plugins.android.application) + alias(libs.plugins.kotlin.android) +} ext { extName = 'Kuramanime' diff --git a/src/id/kuramanime/src/eu/kanade/tachiyomi/animeextension/id/kuramanime/Kuramanime.kt b/src/id/kuramanime/src/eu/kanade/tachiyomi/animeextension/id/kuramanime/Kuramanime.kt index 77b3c06ce5..c4d0da8f2c 100644 --- a/src/id/kuramanime/src/eu/kanade/tachiyomi/animeextension/id/kuramanime/Kuramanime.kt +++ b/src/id/kuramanime/src/eu/kanade/tachiyomi/animeextension/id/kuramanime/Kuramanime.kt @@ -1,7 +1,6 @@ package eu.kanade.tachiyomi.animeextension.id.kuramanime import android.app.Application -import android.content.SharedPreferences import androidx.preference.ListPreference import androidx.preference.PreferenceScreen import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource @@ -12,10 +11,7 @@ import eu.kanade.tachiyomi.animesource.model.Video import eu.kanade.tachiyomi.animesource.online.ParsedAnimeHttpSource import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.util.asJsoup -import okhttp3.OkHttpClient -import okhttp3.Request import okhttp3.Response -import org.jsoup.Jsoup import org.jsoup.nodes.Document import org.jsoup.nodes.Element import uy.kohesive.injekt.Injekt @@ -31,91 +27,102 @@ class Kuramanime : ConfigurableAnimeSource, ParsedAnimeHttpSource() { override val supportsLatest = true - override val client: OkHttpClient = network.cloudflareClient + override val client = network.cloudflareClient - private val preferences: SharedPreferences by lazy { + private val preferences by lazy { Injekt.get().getSharedPreferences("source_$id", 0x0000) } - override fun animeDetailsParse(document: Document): SAnime { - val anime = SAnime.create() - val status = parseStatus(document.select("div.anime__details__widget > div > div:nth-child(1) > ul > li:nth-child(3)").text().replace("Status: ", "")) - anime.title = document.select("div.anime__details__title > h3").text().replace("Judul: ", "") - anime.genre = document.select("div.anime__details__widget > div > div:nth-child(2) > ul > li:nth-child(1)").text().replace("Genre: ", "") - anime.status = status - anime.artist = document.select("div.anime__details__widget > div > div:nth-child(2) > ul > li:nth-child(5)").text() - anime.author = "UNKNOWN" - return anime - } + // ============================== Popular =============================== + override fun popularAnimeRequest(page: Int) = GET("$baseUrl/anime") - private fun parseStatus(statusString: String): Int { - return when (statusString) { - "Sedang Tayang" -> SAnime.ONGOING - "Selesai Tayang" -> SAnime.COMPLETED - else -> SAnime.UNKNOWN - } + override fun popularAnimeSelector() = "div.product__item" + + override fun popularAnimeFromElement(element: Element) = SAnime.create().apply { + setUrlWithoutDomain(element.selectFirst("a")!!.attr("href")) + thumbnail_url = element.selectFirst("a > div")?.attr("data-setbg") + title = element.selectFirst("div.product__item__text > h5")!!.text() } - override fun episodeFromElement(element: Element): SEpisode { - val episode = SEpisode.create() - val epsNum = getNumberFromEpsString(element.text()) - episode.setUrlWithoutDomain(element.attr("href")) - episode.episode_number = when { - epsNum.isNotEmpty() -> epsNum.toFloatOrNull() ?: 1F - else -> 1F - } - episode.name = element.text() + override fun popularAnimeNextPageSelector() = "div.product__pagination > a:last-child" - return episode - } + // =============================== Latest =============================== + override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/anime?order_by=updated&page=$page") - private fun getNumberFromEpsString(epsStr: String): String { - return epsStr.filter { it.isDigit() } - } + override fun latestUpdatesSelector() = popularAnimeSelector() - override fun episodeListSelector(): String = "#episodeLists" + override fun latestUpdatesFromElement(element: Element) = popularAnimeFromElement(element) - override fun episodeListParse(response: Response): List { - val document = response.asJsoup() + override fun latestUpdatesNextPageSelector() = popularAnimeNextPageSelector() - val html = document.select(episodeListSelector()).attr("data-content") - val jsoupE = Jsoup.parse(html) + // =============================== Search =============================== + override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList) = GET("$baseUrl/anime?search=$query&page=$page") - return jsoupE.select("a").filter { ele -> !ele.attr("href").contains("batch") }.map { episodeFromElement(it) }.reversed() - } + override fun searchAnimeSelector() = popularAnimeSelector() - private fun parseShortInfo(element: Element): SAnime { - val anime = SAnime.create() - anime.setUrlWithoutDomain(element.selectFirst("a")!!.attr("href")) - anime.thumbnail_url = element.selectFirst("a > div")!!.attr("data-setbg") - anime.title = element.select("div.product__item__text > h5").text() - return anime - } + override fun searchAnimeFromElement(element: Element) = popularAnimeFromElement(element) - override fun latestUpdatesFromElement(element: Element): SAnime = parseShortInfo(element) + override fun searchAnimeNextPageSelector() = popularAnimeNextPageSelector() - override fun latestUpdatesNextPageSelector(): String = "div.product__pagination > a:last-child" + // =========================== Anime Details ============================ + override fun animeDetailsParse(document: Document) = SAnime.create().apply { + thumbnail_url = document.selectFirst("div.anime__details__pic")?.attr("data-setbg") - override fun latestUpdatesRequest(page: Int): Request = GET("$baseUrl/anime?order_by=updated&page=$page") + val details = document.selectFirst("div.anime__details__text")!! - override fun latestUpdatesSelector(): String = "div.product__item" + title = details.selectFirst("div > h3")!!.text().replace("Judul: ", "") - override fun popularAnimeFromElement(element: Element): SAnime = parseShortInfo(element) + val infos = details.selectFirst("div.anime__details__widget")!! + artist = infos.select("li:contains(Studio:) > a").eachText().joinToString().takeUnless(String::isEmpty) + status = parseStatus(infos.selectFirst("li:contains(Status:) > a")?.text()) - override fun popularAnimeNextPageSelector(): String = "div.product__pagination > a:last-child" + genre = infos.select("li:contains(Genre:) > a, li:contains(Tema:) > a, li:contains(Demografis:) > a") + .eachText() + .joinToString { it.trimEnd(',', ' ') } + .takeUnless(String::isEmpty) - override fun popularAnimeRequest(page: Int): Request = GET("$baseUrl/anime") + description = buildString { + details.selectFirst("p#synopsisField")?.text()?.also(::append) - override fun popularAnimeSelector(): String = "div.product__item" + details.selectFirst("div.anime__details__title > span")?.text() + ?.also { append("\n\nAlternative names: $it\n") } - override fun searchAnimeFromElement(element: Element): SAnime = parseShortInfo(element) + infos.select("ul > li").eachText().forEach { append("\n$it") } + } + } - override fun searchAnimeNextPageSelector(): String = "div.product__pagination > a:last-child" + private fun parseStatus(statusString: String?): Int { + return when (statusString) { + "Sedang Tayang" -> SAnime.ONGOING + "Selesai Tayang" -> SAnime.COMPLETED + else -> SAnime.UNKNOWN + } + } - override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request = GET("$baseUrl/anime?search=$query&page=$page") + // ============================== Episodes ============================== + override fun episodeListParse(response: Response): List { + val document = response.use { it.asJsoup() } + + val html = document.selectFirst(episodeListSelector())?.attr("data-content") + ?: return emptyList() + + val newDoc = response.asJsoup(html) + + return newDoc.select("a") + .filterNot { it.attr("href").contains("batch") } + .map(::episodeFromElement) + .reversed() + } - override fun searchAnimeSelector(): String = "div.product__item" + override fun episodeListSelector() = "a#episodeLists" + override fun episodeFromElement(element: Element) = SEpisode.create().apply { + setUrlWithoutDomain(element.attr("href")) + name = element.text() + episode_number = name.filter(Char::isDigit).toFloatOrNull() ?: 1F + } + + // ============================ Video Links ============================= override fun videoListSelector() = "video#player > source" override fun videoListParse(response: Response): List