diff --git a/src/en/hahomoe/AndroidManifest.xml b/src/en/hahomoe/AndroidManifest.xml new file mode 100644 index 000000000..acb4de356 --- /dev/null +++ b/src/en/hahomoe/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/src/en/hahomoe/build.gradle b/src/en/hahomoe/build.gradle new file mode 100644 index 000000000..3a420d10e --- /dev/null +++ b/src/en/hahomoe/build.gradle @@ -0,0 +1,13 @@ +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' + +ext { + extName = 'haho.moe' + pkgNameSuffix = 'en.hahomoe' + extClass = '.HahoMoe' + extVersionCode = 1 + libVersion = '12' + containsNsfw = true +} + +apply from: "$rootDir/common.gradle" 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 new file mode 100644 index 000000000..0616828f8 --- /dev/null +++ b/src/en/hahomoe/src/eu/kanade/tachiyomi/animeextension/en/hahomoe/HahoMoe.kt @@ -0,0 +1,167 @@ +package eu.kanade.tachiyomi.animeextension.en.hahomoe + +import android.annotation.SuppressLint +import android.util.Log +import eu.kanade.tachiyomi.animesource.model.AnimeFilterList +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.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.text.SimpleDateFormat +import java.util.Date +import kotlin.collections.ArrayList + +class HahoMoe : ParsedAnimeHttpSource() { + + override val name = "haho.moe" + + override val baseUrl = "https://haho.moe" + + override val lang = "en" + + override val supportsLatest = true + + override val client: OkHttpClient = network.cloudflareClient + + override fun popularAnimeSelector(): String = "ul.anime-loop.loop li a" + + override fun popularAnimeRequest(page: Int): Request = GET("$baseUrl/anime?s=vdy-d&page=$page") + + 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 popularAnimeNextPageSelector(): String = "ul.pagination li.page-item a[rel=next]" + + override fun episodeListSelector() = "ul.episode-loop li a" + + private fun episodeNextPageSelector() = popularAnimeNextPageSelector() + + 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()) } + } + + addEpisodes(response.asJsoup()) + return episodes + } + + 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 + } + 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 + } + } + return Date(-1L) + } + + override fun videoListParse(response: Response): List