diff --git a/Anifreakz/build.gradle.kts b/Anifreakz/build.gradle.kts
new file mode 100644
index 00000000..0ec970cf
--- /dev/null
+++ b/Anifreakz/build.gradle.kts
@@ -0,0 +1,27 @@
+// use an integer for version numbers
+version = 1
+
+
+cloudstream {
+ language = "de"
+ // 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=anifreakz.com&sz=%size%"
+}
\ No newline at end of file
diff --git a/Anifreakz/src/main/AndroidManifest.xml b/Anifreakz/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..c98063f8
--- /dev/null
+++ b/Anifreakz/src/main/AndroidManifest.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/Anifreakz/src/main/kotlin/com/hexated/Anifreakz.kt b/Anifreakz/src/main/kotlin/com/hexated/Anifreakz.kt
new file mode 100644
index 00000000..32d9b7e4
--- /dev/null
+++ b/Anifreakz/src/main/kotlin/com/hexated/Anifreakz.kt
@@ -0,0 +1,193 @@
+package com.hexated
+
+import com.lagradost.cloudstream3.*
+import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
+import com.lagradost.cloudstream3.utils.ExtractorLink
+import com.lagradost.cloudstream3.utils.Qualities
+import com.lagradost.cloudstream3.utils.getAndUnpack
+import org.jsoup.nodes.Element
+import java.net.URLDecoder
+
+class Anifreakz : MainAPI() {
+ override var mainUrl = "https://anifreakz.com"
+ override var name = "Anifreakz"
+ override val hasMainPage = true
+ override var lang = "de"
+ override val hasDownloadSupport = true
+
+ override val supportedTypes = setOf(
+ TvType.Anime,
+ TvType.AnimeMovie,
+ TvType.OVA
+ )
+
+ override val mainPage = mainPageOf(
+ "$mainUrl/series?filter=null&page=" to "Anime Neuste",
+ "$mainUrl/series?filter={\"sorting\":\"popular\"}&page=" to "Anime Angesagt",
+ "$mainUrl/series?filter={\"sorting\":\"released\"}&page=" to "Anime Veröffentlicht",
+ "$mainUrl/kalender" to "Anime Kalender",
+ )
+
+ override suspend fun getMainPage(
+ page: Int,
+ request: MainPageRequest
+ ): HomePageResponse {
+ val items = mutableListOf()
+ val nonPaged = request.name == "Anime Kalender" && page <= 1
+
+ if (!nonPaged) {
+ val document = app.get(request.data + page).document
+ val home = document.select("div.app-section div.col").mapNotNull {
+ it.toSearchResult()
+ }
+ items.add(HomePageList(request.name, home))
+ }
+
+ if (nonPaged) {
+ val document = app.get(request.data).document
+ document.select("div.app-content div.app-section").forEach { block ->
+ val header = block.selectFirst("div.app-heading div.text")?.ownText() ?: ""
+ val home = block.select("div.col").mapNotNull {
+ it.toSearchResult()
+ }
+ items.add(HomePageList(header, home))
+ }
+ }
+
+ return newHomePageResponse(items)
+
+ }
+
+ private fun Element.toSearchResult(): AnimeSearchResponse? {
+ val href = fixUrl(this.selectFirst("a")?.attr("href") ?: return null)
+ val title = this.selectFirst("a.list-title")?.text()?.trim() ?: return null
+ val posterUrl = fixUrlNull(this.selectFirst("div.media-cover")?.attr("data-src"))
+
+ return newAnimeSearchResponse(title, href, TvType.Anime) {
+ this.posterUrl = posterUrl
+ }
+ }
+
+ override suspend fun search(query: String): List {
+ val document = app.get("$mainUrl/search/$query").document
+ return document.select("div.app-section div.col").mapNotNull {
+ it.toSearchResult()
+ }
+ }
+
+ private fun decode(input: String): String? = URLDecoder.decode(input, "utf-8")
+
+ override suspend fun load(url: String): LoadResponse? {
+ val document = app.get(url).document
+
+ val title =
+ document.selectFirst("div.pl-md-4 h1, div.caption-content h1")?.text() ?: return null
+ val poster = fixUrlNull(document.selectFirst("div.media.media-cover")?.attr("data-src"))
+ val tags = document.select("div.categories a").map { it.text() }
+ val rating = document.selectFirst("div.featured-box div:contains(IMDB) div.text")?.text()
+ .toRatingInt()
+ val year =
+ document.selectFirst("div.featured-box div:contains(Veröffentlicht) div.text")?.text()
+ ?.trim()?.toIntOrNull()
+ val type = if (document.select("div.episodes.tab-content")
+ .isNullOrEmpty()
+ ) TvType.Movie else TvType.TvSeries
+ val description =
+ document.select("div.detail-attr div.text-content, div.caption div:contains(Übersicht) div.text")
+ .text().trim()
+ val trailer = document.selectFirst("button:contains(Trailer)")?.attr("data-remote")
+ ?.substringAfter("?trailer=")?.let {
+ decode(it)
+ }
+
+ return if (type == TvType.Movie) {
+ newMovieLoadResponse(title, url, type, url) {
+ posterUrl = poster
+ this.year = year
+ this.rating = rating
+ plot = description
+ this.tags = tags
+ addTrailer(trailer)
+ }
+ } else {
+ val subEpisodes = mutableListOf()
+ document.select("div.episodes.tab-content div[id*=season-]").forEach { season ->
+ val seasonNum = season.attr("id").substringAfterLast("-").toIntOrNull()
+ season.select("a").map { eps ->
+ val href = eps.attr("href")
+ val name = eps.select("div.name").text()
+ val episode =
+ eps.select("div.episode").text().substringBefore(".").toIntOrNull()
+ val episodes = Episode(
+ href,
+ name,
+ seasonNum,
+ episode
+ )
+ subEpisodes.add(episodes)
+ }
+ }
+ newAnimeLoadResponse(title, url, type) {
+ posterUrl = poster
+ this.year = year
+ addEpisodes(DubStatus.Subbed, subEpisodes)
+ this.rating = rating
+ plot = description
+ this.tags = tags
+ addTrailer(trailer)
+ }
+ }
+
+ }
+
+ override suspend fun loadLinks(
+ data: String,
+ isCasting: Boolean,
+ subtitleCallback: (SubtitleFile) -> Unit,
+ callback: (ExtractorLink) -> Unit
+ ): Boolean {
+ val document = app.get(data).document
+
+ val servers = mutableListOf>()
+
+ document.select("div[aria-labelledby=videoSource] button").map {
+ servers.add(it.attr("data-embed") to it.text())
+ }
+
+ document.select("div.nav-player-select a").map {
+ servers.add(it.attr("data-embed") to it.select("span").text())
+ }
+
+ servers.distinctBy { it.first }.apmap { (id, source) ->
+ val iframe = app.post(
+ "$mainUrl/ajax/embed",
+ data = mapOf("id" to id, "captcha" to ""),
+ headers = mapOf(
+ "X-Requested-With" to "XMLHttpRequest"
+ ),
+ referer = data
+ ).document.select("iframe").attr("src")
+ val doc = app.get(iframe, referer = "$mainUrl/").document
+ val link =
+ unpackJs(doc)?.substringAfter("file:\"")?.substringBefore("\"") ?: return@apmap null
+ callback.invoke(
+ ExtractorLink(
+ source,
+ source,
+ link,
+ "https://filemoon.sx/",
+ Qualities.Unknown.value,
+ isM3u8 = link.contains(".m3u8")
+ )
+ )
+ }
+
+ return true
+ }
+
+ private fun unpackJs(script: Element): String? {
+ return script.select("script").find { it.data().contains("eval(function(p,a,c,k,e,d)") }
+ ?.data()?.let { getAndUnpack(it) }
+ }
+
+}
\ No newline at end of file
diff --git a/Anifreakz/src/main/kotlin/com/hexated/AnifreakzPlugin.kt b/Anifreakz/src/main/kotlin/com/hexated/AnifreakzPlugin.kt
new file mode 100644
index 00000000..5de45031
--- /dev/null
+++ b/Anifreakz/src/main/kotlin/com/hexated/AnifreakzPlugin.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 AnifreakzPlugin: Plugin() {
+ override fun load(context: Context) {
+ // All providers should be added in this manner. Please don't edit the providers list directly.
+ registerMainAPI(Anifreakz())
+ }
+}
\ No newline at end of file