diff --git a/src/pt/anidong/AndroidManifest.xml b/src/pt/anidong/AndroidManifest.xml
new file mode 100644
index 000000000..2970f0d26
--- /dev/null
+++ b/src/pt/anidong/AndroidManifest.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/pt/anidong/build.gradle b/src/pt/anidong/build.gradle
new file mode 100644
index 000000000..5a4f19762
--- /dev/null
+++ b/src/pt/anidong/build.gradle
@@ -0,0 +1,14 @@
+plugins {
+ alias(libs.plugins.android.application)
+ alias(libs.plugins.kotlin.android)
+ alias(libs.plugins.kotlin.serialization)
+}
+
+ext {
+ extName = 'AniDong'
+ pkgNameSuffix = 'pt.anidong'
+ extClass = '.AniDong'
+ extVersionCode = 1
+}
+
+apply from: "$rootDir/common.gradle"
diff --git a/src/pt/anidong/res/mipmap-hdpi/ic_launcher.png b/src/pt/anidong/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 000000000..50be877d9
Binary files /dev/null and b/src/pt/anidong/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/src/pt/anidong/res/mipmap-mdpi/ic_launcher.png b/src/pt/anidong/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 000000000..b23fff3ab
Binary files /dev/null and b/src/pt/anidong/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/src/pt/anidong/res/mipmap-xhdpi/ic_launcher.png b/src/pt/anidong/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..71ddb7b8d
Binary files /dev/null and b/src/pt/anidong/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/src/pt/anidong/res/mipmap-xxhdpi/ic_launcher.png b/src/pt/anidong/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 000000000..720b33eba
Binary files /dev/null and b/src/pt/anidong/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/src/pt/anidong/res/mipmap-xxxhdpi/ic_launcher.png b/src/pt/anidong/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 000000000..a0cb538f7
Binary files /dev/null and b/src/pt/anidong/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/src/pt/anidong/src/eu/kanade/tachiyomi/animeextension/pt/anidong/AniDong.kt b/src/pt/anidong/src/eu/kanade/tachiyomi/animeextension/pt/anidong/AniDong.kt
new file mode 100644
index 000000000..da8f6d7ac
--- /dev/null
+++ b/src/pt/anidong/src/eu/kanade/tachiyomi/animeextension/pt/anidong/AniDong.kt
@@ -0,0 +1,258 @@
+package eu.kanade.tachiyomi.animeextension.pt.anidong
+
+import eu.kanade.tachiyomi.animeextension.pt.anidong.dto.EpisodeDto
+import eu.kanade.tachiyomi.animeextension.pt.anidong.dto.EpisodeListDto
+import eu.kanade.tachiyomi.animeextension.pt.anidong.dto.SearchResultDto
+import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
+import eu.kanade.tachiyomi.animesource.model.AnimesPage
+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.network.POST
+import eu.kanade.tachiyomi.network.asObservableSuccess
+import eu.kanade.tachiyomi.util.asJsoup
+import kotlinx.serialization.decodeFromString
+import kotlinx.serialization.json.Json
+import okhttp3.FormBody
+import okhttp3.Request
+import okhttp3.Response
+import org.jsoup.nodes.Document
+import org.jsoup.nodes.Element
+import rx.Observable
+import uy.kohesive.injekt.injectLazy
+
+class AniDong : ParsedAnimeHttpSource() {
+
+ override val name = "AniDong"
+
+ override val baseUrl = "https://anidong.net"
+
+ override val lang = "pt-BR"
+
+ override val supportsLatest = true
+
+ private val json: Json by injectLazy()
+
+ private val apiHeaders by lazy {
+ headersBuilder() // sets user-agent
+ .add("Referer", baseUrl)
+ .add("x-requested-with", "XMLHttpRequest")
+ .build()
+ }
+
+ // ============================== Popular ===============================
+ override fun popularAnimeFromElement(element: Element) = SAnime.create().apply {
+ setUrlWithoutDomain(element.attr("href"))
+ title = element.attr("title")
+ thumbnail_url = element.selectFirst("img")?.attr("src")
+ }
+
+ override fun popularAnimeNextPageSelector() = null
+ override fun popularAnimeRequest(page: Int) = GET(baseUrl)
+ override fun popularAnimeSelector() = "article.top10_animes_item > a"
+
+ // ============================== Episodes ==============================
+ override fun episodeFromElement(element: Element): SEpisode {
+ throw UnsupportedOperationException("Not used.")
+ }
+
+ override fun episodeListSelector(): String {
+ throw UnsupportedOperationException("Not used.")
+ }
+
+ override fun episodeListParse(response: Response): List {
+ val doc = getRealDoc(response.asJsoup())
+
+ val id = doc.selectFirst("link[rel=shortlink]")!!.attr("href").substringAfter("=")
+ val body = FormBody.Builder()
+ .add("action", "show_videos")
+ .add("anime_id", id)
+ .build()
+
+ val res = client.newCall(POST("$baseUrl/api", headers = apiHeaders, body = body)).execute()
+ val data = json.decodeFromString(res.body.string())
+
+ return buildList {
+ data.episodes.forEach { add(episodeFromObject(it, "Episódio")) }
+ data.movies.forEach { add(episodeFromObject(it, "Filme")) }
+ data.ovas.forEach { add(episodeFromObject(it, "OVA")) }
+ sortByDescending { it.episode_number }
+ }
+ }
+
+ private fun episodeFromObject(episode: EpisodeDto, prefix: String) = SEpisode.create().apply {
+ setUrlWithoutDomain(episode.epi_url)
+ episode_number = episode.epi_num.toFloatOrNull() ?: 0F
+ name = "$prefix ${episode.epi_num}"
+ }
+
+ // =========================== Anime Details ============================
+ override fun animeDetailsParse(document: Document) = SAnime.create().apply {
+ val doc = getRealDoc(document)
+ val infos = doc.selectFirst("div.anime_infos")!!
+
+ setUrlWithoutDomain(doc.location())
+ title = infos.selectFirst("div > h3")!!.ownText()
+ thumbnail_url = infos.selectFirst("img")!!.attr("src")
+ genre = infos.select("div[itemprop=genre] a").eachText().joinToString()
+ artist = infos.selectFirst("div[itemprop=productionCompany]")!!.text()
+
+ status = doc.selectFirst("div:contains(Status) span")?.text().let {
+ when {
+ it == null -> SAnime.UNKNOWN
+ it == "Completo" -> SAnime.COMPLETED
+ it.contains("Lançamento") -> SAnime.ONGOING
+ else -> SAnime.UNKNOWN
+ }
+ }
+
+ description = buildString {
+ infos.selectFirst("div.anime_name + div.anime_info")?.text()?.let {
+ append("Nomes alternativos: $it\n")
+ }
+
+ doc.selectFirst("div[itemprop=description]")?.text()?.let {
+ append("\n$it")
+ }
+ }
+ }
+
+ // ============================ Video Links =============================
+ override fun videoListParse(response: Response): List