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