diff --git a/YugenAnime/build.gradle.kts b/YugenAnime/build.gradle.kts
new file mode 100644
index 00000000..17c649ad
--- /dev/null
+++ b/YugenAnime/build.gradle.kts
@@ -0,0 +1,27 @@
+// use an integer for version numbers
+version = 1
+
+
+cloudstream {
+ language = "en"
+ // All of these properties are optional, you can safely remove them
+
+ // description = "Lorem Ipsum"
+ authors = listOf("Hexated")
+
+ /**
+ * Status int as the following:
+ * 0: Down
+ * 1: Ok
+ * 2: Slow
+ * 3: Beta only
+ * */
+ status = 1 // will be 3 if unspecified
+ tvTypes = listOf(
+ "AnimeMovie",
+ "Anime",
+ "OVA",
+ )
+
+ iconUrl = "https://www.google.com/s2/favicons?domain=yugen.to&sz=%size%"
+}
\ No newline at end of file
diff --git a/YugenAnime/src/main/AndroidManifest.xml b/YugenAnime/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..874740e3
--- /dev/null
+++ b/YugenAnime/src/main/AndroidManifest.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/YugenAnime/src/main/kotlin/com/hexated/YugenAnime.kt b/YugenAnime/src/main/kotlin/com/hexated/YugenAnime.kt
new file mode 100644
index 00000000..81f09016
--- /dev/null
+++ b/YugenAnime/src/main/kotlin/com/hexated/YugenAnime.kt
@@ -0,0 +1,182 @@
+package com.hexated
+
+import com.fasterxml.jackson.annotation.JsonProperty
+import com.lagradost.cloudstream3.*
+import com.lagradost.cloudstream3.LoadResponse.Companion.addAniListId
+import com.lagradost.cloudstream3.LoadResponse.Companion.addMalId
+import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
+import com.lagradost.cloudstream3.utils.ExtractorLink
+import com.lagradost.cloudstream3.utils.M3u8Helper
+import org.jsoup.nodes.Document
+import org.jsoup.nodes.Element
+import java.net.URI
+
+class YugenAnime : MainAPI() {
+ override var mainUrl = "https://yugen.to"
+ override var name = "YugenAnime"
+ override val hasMainPage = true
+ override var lang = "en"
+ override val hasDownloadSupport = true
+
+ override val supportedTypes = setOf(
+ TvType.Anime,
+ TvType.AnimeMovie,
+ TvType.OVA
+ )
+
+ companion object {
+ fun getType(t: String): TvType {
+ return if (t.contains("OVA", true) || t.contains("Special", true)) TvType.OVA
+ else if (t.contains("Movie", true)) TvType.AnimeMovie
+ else TvType.Anime
+ }
+
+ fun getStatus(t: String): ShowStatus {
+ return when (t) {
+ "Finished Airing" -> ShowStatus.Completed
+ "Currently Airing" -> ShowStatus.Ongoing
+ else -> ShowStatus.Completed
+ }
+ }
+ }
+
+ override val mainPage = mainPageOf(
+ "$mainUrl/trending/?page=" to "Trending",
+ "$mainUrl/latest/?page=" to "Recently Released",
+ "$mainUrl/best/?page=" to "Most Popular Series",
+ "$mainUrl/new/?page=" to "New to YugenAnime",
+ )
+
+ override suspend fun getMainPage(
+ page: Int,
+ request: MainPageRequest
+ ): HomePageResponse {
+ val items = mutableListOf()
+ val document = app.get(request.data + page).document
+ val home = document.select("div.cards-grid a, ul.ep-grid li.ep-card").mapNotNull {
+ it.toSearchResult()
+ }
+ items.add(HomePageList(request.name, home, request.name == "Recently Released"))
+ return newHomePageResponse(items)
+ }
+
+ private fun Element.toSearchResult(): AnimeSearchResponse? {
+ val title = this.attr("title").ifBlank { this.select("div.ep-origin-name").text() }
+ .ifBlank { this.select("span.anime-name").text() } ?: return null
+ val href = fixUrl(this.attr("href").ifBlank { this.select("a.ep-details").attr("href") })
+ val posterUrl = fixUrlNull(this.selectFirst("img.lozad")?.attr("data-src"))
+ val epNum =
+ this.select("a.ep-thumbnail").attr("title").substringBefore(":").filter { it.isDigit() }
+ .toIntOrNull()
+ return newAnimeSearchResponse(title, href, TvType.Anime) {
+ this.posterUrl = posterUrl
+ addDubStatus(dubExist = true, subExist = true, dubEpisodes = epNum, subEpisodes = epNum)
+ }
+ }
+
+ override suspend fun search(query: String): List {
+ val document = app.get("$mainUrl/search/?q=$query").document
+ return document.select("div.cards-grid a.anime-meta").mapNotNull {
+ it.toSearchResult()
+ }
+ }
+
+ override suspend fun load(url: String): LoadResponse? {
+ val document = app.get(url).document
+
+ val title = document.selectFirst("div.content h1")?.text() ?: return null
+ val poster = document.selectFirst("img.cover")?.attr("src")
+ val tags = document.getPageContent("Genres").split(",").map { it.trim() }
+ val type = getType(document.getPageContent("Format"))
+ val year = document.getPageContent("Premiered").filter { it.isDigit() }.toIntOrNull()
+ val status = getStatus(document.getPageContent("Status"))
+ val description = document.select("p.description").text()
+
+ val malId = document.getExternalId("MyAnimeList")
+ val anilistId = document.getExternalId("AniList")
+
+ val trailer = document.selectFirst("iframe.lozad.video")?.attr("src")
+
+ val episodes = app.get("${url}watch").document.select("ul.ep-grid li.ep-card").map { eps ->
+ val epsTitle = eps.select("a.ep-title").text()
+ val link = fixUrl(eps.select("a.ep-title").attr("href"))
+ val episode = epsTitle.substringBefore(":").filter { it.isDigit() }.toIntOrNull()
+ Episode(link, name = epsTitle.substringAfter(":").trim(), episode = episode)
+ }
+
+ return newAnimeLoadResponse(title, url, type) {
+ engName = title
+ posterUrl = poster
+ this.year = year
+ addEpisodes(DubStatus.Subbed, episodes)
+ showStatus = status
+ plot = description
+ this.tags = tags
+ addMalId(malId)
+ addAniListId(anilistId)
+ addTrailer(trailer)
+ }
+ }
+
+ override suspend fun loadLinks(
+ data: String,
+ isCasting: Boolean,
+ subtitleCallback: (SubtitleFile) -> Unit,
+ callback: (ExtractorLink) -> Unit
+ ): Boolean {
+
+ val episode = data.removeSuffix("/").split("/").last()
+ val dubData = data.substringBeforeLast("/$episode").let { "$it-dub/$episode" }
+
+ listOf(data, dubData).apmap { url ->
+ val doc = app.get(url).document
+ val iframe = doc.select("iframe#main-embed").attr("src") ?: return@apmap null
+ val id = iframe.removeSuffix("/").split("/").lastOrNull() ?: return@apmap null
+ val source = app.post(
+ "https://yugen.to/api/embed/", data = mapOf(
+ "id" to id,
+ "ac" to "0"
+ ), referer = iframe,
+ headers = mapOf("X-Requested-With" to "XMLHttpRequest")
+ ).parsedSafe()?.hls?.distinct()?.firstOrNull() ?: return@apmap null
+
+ val isDub = if (url.contains("-dub")) "dub" else "sub"
+
+ M3u8Helper.generateM3u8(
+ "${getSourceType(getBaseUrl(source))} [$isDub]",
+ source,
+ ""
+ ).forEach(callback)
+ }
+
+ return true
+ }
+
+ private fun Document.getExternalId(str: String): Int? {
+ return this.select("div.anime-metadetails > div:contains(External Links) a:contains($str)")
+ .attr("href").removeSuffix("/").split("/").lastOrNull()?.toIntOrNull()
+ }
+
+ private fun Document.getPageContent(str: String): String {
+ return this.select("div.anime-metadetails > div:contains($str) span.description").text()
+ }
+
+ private fun getBaseUrl(url: String): String {
+ return URI(url).let {
+ "${it.scheme}://${it.host}"
+ }
+ }
+
+ private fun getSourceType(url: String): String {
+ return when {
+ url.contains("vrv", true) -> "Vrv"
+ url.contains("gofcdn", true) -> "Gofcdn"
+ else -> this.name
+ }
+ }
+
+ data class Sources(
+ @JsonProperty("hls") val hls: List? = null,
+ )
+
+}
\ No newline at end of file
diff --git a/YugenAnime/src/main/kotlin/com/hexated/YugenAnimePlugin.kt b/YugenAnime/src/main/kotlin/com/hexated/YugenAnimePlugin.kt
new file mode 100644
index 00000000..c3cd6973
--- /dev/null
+++ b/YugenAnime/src/main/kotlin/com/hexated/YugenAnimePlugin.kt
@@ -0,0 +1,14 @@
+
+package com.hexated
+
+import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
+import com.lagradost.cloudstream3.plugins.Plugin
+import android.content.Context
+
+@CloudstreamPlugin
+class YugenAnimePlugin: Plugin() {
+ override fun load(context: Context) {
+ // All providers should be added in this manner. Please don't edit the providers list directly.
+ registerMainAPI(YugenAnime())
+ }
+}
\ No newline at end of file