From c33129df3a0620ebd7c3b617fda20d13edaad59f Mon Sep 17 00:00:00 2001 From: Claudemirovsky <63046606+Claudemirovsky@users.noreply.github.com> Date: Fri, 10 Nov 2023 23:32:23 -0300 Subject: [PATCH 1/4] refactor: General refactoration --- src/en/hahomoe/build.gradle | 6 +- .../animeextension/en/hahomoe/HahoMoe.kt | 942 ++---------------- .../en/hahomoe/HahoMoeFilters.kt | 740 ++++++++++++++ 3 files changed, 842 insertions(+), 846 deletions(-) create mode 100644 src/en/hahomoe/src/eu/kanade/tachiyomi/animeextension/en/hahomoe/HahoMoeFilters.kt diff --git a/src/en/hahomoe/build.gradle b/src/en/hahomoe/build.gradle index fbb7e54419..735d41e831 100644 --- a/src/en/hahomoe/build.gradle +++ b/src/en/hahomoe/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 = 'haho.moe' diff --git a/src/en/hahomoe/src/eu/kanade/tachiyomi/animeextension/en/hahomoe/HahoMoe.kt b/src/en/hahomoe/src/eu/kanade/tachiyomi/animeextension/en/hahomoe/HahoMoe.kt index 2f73f0f19d..54f9f7b7ac 100644 --- a/src/en/hahomoe/src/eu/kanade/tachiyomi/animeextension/en/hahomoe/HahoMoe.kt +++ b/src/en/hahomoe/src/eu/kanade/tachiyomi/animeextension/en/hahomoe/HahoMoe.kt @@ -1,7 +1,5 @@ package eu.kanade.tachiyomi.animeextension.en.hahomoe -import android.annotation.SuppressLint -import eu.kanade.tachiyomi.animesource.model.AnimeFilter import eu.kanade.tachiyomi.animesource.model.AnimeFilterList import eu.kanade.tachiyomi.animesource.model.SAnime import eu.kanade.tachiyomi.animesource.model.SEpisode @@ -9,18 +7,14 @@ 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.Headers.Companion.toHeaders -import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.Response import org.jsoup.nodes.Document import org.jsoup.nodes.Element import java.lang.Exception -import java.lang.Float.parseFloat +import java.net.URLEncoder import java.text.SimpleDateFormat -import java.util.Date import java.util.Locale -import kotlin.collections.ArrayList class HahoMoe : ParsedAnimeHttpSource() { @@ -32,83 +26,113 @@ class HahoMoe : ParsedAnimeHttpSource() { override val supportsLatest = true - override val client: OkHttpClient = network.cloudflareClient + override val client = network.cloudflareClient - override fun popularAnimeSelector(): String = "ul.anime-loop.loop li a" + // ============================== Popular =============================== + override fun popularAnimeRequest(page: Int) = GET("$baseUrl/anime?s=vdy-d&page=$page") - override fun popularAnimeRequest(page: Int): Request = GET("$baseUrl/anime?s=vdy-d&page=$page") + override fun popularAnimeSelector() = "ul.anime-loop.loop li a" - override fun popularAnimeFromElement(element: Element): SAnime { - val anime = SAnime.create() - anime.setUrlWithoutDomain(element.attr("href") + "?s=srt-d") - anime.title = element.select("div span").not(".badge").text() - return anime + override fun popularAnimeFromElement(element: Element) = SAnime.create().apply { + setUrlWithoutDomain(element.attr("href") + "?s=srt-d") + title = element.selectFirst("div.label > span, div span.thumb-title")!!.text() } - override fun popularAnimeNextPageSelector(): String = "ul.pagination li.page-item a[rel=next]" + override fun popularAnimeNextPageSelector() = "ul.pagination li.page-item a[rel=next]" - override fun episodeListSelector() = "ul.episode-loop li a" + // =============================== Latest =============================== + override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/anime?s=rel-d&page=$page") - private fun episodeNextPageSelector() = popularAnimeNextPageSelector() + override fun latestUpdatesSelector() = popularAnimeSelector() - override fun episodeListParse(response: Response): List { - val episodes = mutableListOf() - fun addEpisodes(document: Document) { - document.select(episodeListSelector()).map { episodes.add(episodeFromElement(it)) } - document.select(episodeNextPageSelector()).firstOrNull() - ?.let { addEpisodes(client.newCall(GET(it.attr("href"), headers)).execute().asJsoup()) } - } + override fun latestUpdatesFromElement(element: Element) = popularAnimeFromElement(element) + + override fun latestUpdatesNextPageSelector() = popularAnimeNextPageSelector() + + // =============================== Search =============================== + override fun getFilterList() = HahoMoeFilters.FILTER_LIST + + override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request { + val (includedTags, excludedTags, orderBy, ordering) = HahoMoeFilters.getSearchParameters(filters) + + val httpQuery = buildString { + if (query.isNotBlank()) append(query.trim()) + if (includedTags.isNotEmpty()) { + append(includedTags.joinToString(" genre:", prefix = " genre:")) + } + if (excludedTags.isNotEmpty()) { + append(excludedTags.joinToString(" -genre:", prefix = " -genre:")) + } + }.let { URLEncoder.encode(it, "UTF-8") } + + return GET("$baseUrl/anime?page=$page&s=$orderBy$ordering&q=$httpQuery") + } - addEpisodes(response.asJsoup()) - return episodes + 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 { + setUrlWithoutDomain(document.location()) + thumbnail_url = document.selectFirst("img.cover-image.img-thumbnail")?.absUrl("src") + title = document.selectFirst("li.breadcrumb-item.active")!!.text() + genre = document.select("li.genre span.value").joinToString { it.text() } + description = document.selectFirst("div.card-body")?.text() + author = document.select("li.production span.value").joinToString { it.text() } + artist = document.selectFirst("li.group span.value")?.text() + status = parseStatus(document.selectFirst("li.status span.value")?.text()) } - override fun episodeFromElement(element: Element): SEpisode { - val episode = SEpisode.create() - episode.setUrlWithoutDomain(element.attr("href")) - val episodeNumberString = element.select("div.episode-number").text().removePrefix("Episode ") - var numeric = true - try { - parseFloat(episodeNumberString) - } catch (e: NumberFormatException) { - numeric = false + private fun parseStatus(statusString: String?): Int { + return when (statusString) { + "Ongoing" -> SAnime.ONGOING + "Completed" -> SAnime.COMPLETED + else -> SAnime.UNKNOWN } - episode.episode_number = if (numeric) episodeNumberString.toFloat() else element.parent()!!.className().removePrefix("episode").toFloat() - episode.name = element.select("div.episode-number").text() + ": " + element.select("div.episode-label").text() + element.select("div.episode-title").text() - val date: String = element.select("div.date").text() - val parsedDate = parseDate(date) - if (parsedDate.time != -1L) episode.date_upload = parsedDate.time - return episode } - @SuppressLint("SimpleDateFormat") - private fun parseDate(date: String): Date { - val knownPatterns: MutableList = ArrayList() - knownPatterns.add(SimpleDateFormat("dd'th of 'MMM, yyyy")) - knownPatterns.add(SimpleDateFormat("dd'nd of 'MMM, yyyy")) - knownPatterns.add(SimpleDateFormat("dd'st of 'MMM, yyyy")) - knownPatterns.add(SimpleDateFormat("dd'rd of 'MMM, yyyy")) - - for (pattern in knownPatterns) { - try { - // Take a try - return Date(pattern.parse(date)!!.time) - } catch (e: Throwable) { - // Loop on - } + // ============================== Episodes ============================== + override fun episodeListSelector() = "ul.episode-loop li a" + + private fun episodeNextPageSelector() = popularAnimeNextPageSelector() + + override fun episodeListParse(response: Response): List { + var doc = response.use { it.asJsoup() } + return buildList { + do { + if (isNotEmpty()) { + val url = doc.selectFirst(episodeNextPageSelector())!!.absUrl("href") + doc = client.newCall(GET(url)).execute().use { it.asJsoup() } + } + + doc.select(episodeListSelector()) + .map(::episodeFromElement) + .also(::addAll) + } while (doc.selectFirst(episodeNextPageSelector()) != null) } - return Date(-1L) } + override fun episodeFromElement(element: Element) = SEpisode.create().apply { + setUrlWithoutDomain(element.attr("href")) + + val episodeNumberString = element.selectFirst("div.episode-number")!!.text() + episode_number = episodeNumberString.removePrefix("Episode ").toFloatOrNull() ?: 1F + name = "$episodeNumberString: " + element.selectFirst("div.episode-label")?.text().orEmpty() + date_upload = element.selectFirst("div.date")?.text().orEmpty().toDate() + } + + // ============================ Video Links ============================= override fun videoListParse(response: Response): List