diff --git a/AnimeONProvider/build.gradle.kts b/AnimeONProvider/build.gradle.kts
new file mode 100644
index 0000000..1b6607c
--- /dev/null
+++ b/AnimeONProvider/build.gradle.kts
@@ -0,0 +1,30 @@
+// use an integer for version numbers
+version = 1
+
+dependencies{
+ implementation("com.google.code.gson:gson:2.9.0")
+}
+
+cloudstream {
+ language = "uk"
+ // All of these properties are optional, you can safely remove them
+
+ description = "AnimeON - Дивитися аніме україньскою"
+ authors = listOf("CakesTwix")
+
+ /**
+ * Status int as the following:
+ * 0: Down
+ * 1: Ok
+ * 2: Slow
+ * 3: Beta only
+ * */
+ status = 1 // will be 3 if unspecified
+ tvTypes = listOf(
+ "Anime",
+ "AnimeMovie",
+ "OVA",
+ )
+
+ iconUrl = "https://www.google.com/s2/favicons?domain=animeon.club&sz=%size%"
+}
\ No newline at end of file
diff --git a/AnimeONProvider/src/main/AndroidManifest.xml b/AnimeONProvider/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..29aec9d
--- /dev/null
+++ b/AnimeONProvider/src/main/AndroidManifest.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/AnimeONProvider/src/main/kotlin/com/lagradost/AnimeONProvider.kt b/AnimeONProvider/src/main/kotlin/com/lagradost/AnimeONProvider.kt
new file mode 100644
index 0000000..2b8801a
--- /dev/null
+++ b/AnimeONProvider/src/main/kotlin/com/lagradost/AnimeONProvider.kt
@@ -0,0 +1,248 @@
+package com.lagradost
+
+import com.google.gson.Gson
+import com.google.gson.reflect.TypeToken
+import com.lagradost.cloudstream3.DubStatus
+import com.lagradost.cloudstream3.Episode
+import com.lagradost.cloudstream3.HomePageResponse
+import com.lagradost.cloudstream3.LoadResponse
+import com.lagradost.cloudstream3.LoadResponse.Companion.addMalId
+import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
+import com.lagradost.cloudstream3.MainAPI
+import com.lagradost.cloudstream3.MainPageRequest
+import com.lagradost.cloudstream3.SearchResponse
+import com.lagradost.cloudstream3.ShowStatus
+import com.lagradost.cloudstream3.SubtitleFile
+import com.lagradost.cloudstream3.TvType
+import com.lagradost.cloudstream3.addDubStatus
+import com.lagradost.cloudstream3.addEpisodes
+import com.lagradost.cloudstream3.app
+import com.lagradost.cloudstream3.mainPageOf
+import com.lagradost.cloudstream3.newAnimeLoadResponse
+import com.lagradost.cloudstream3.newAnimeSearchResponse
+import com.lagradost.cloudstream3.newHomePageResponse
+import com.lagradost.cloudstream3.newMovieLoadResponse
+import com.lagradost.cloudstream3.toRatingInt
+import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
+import com.lagradost.cloudstream3.utils.ExtractorLink
+import com.lagradost.cloudstream3.utils.M3u8Helper
+import com.lagradost.models.AnimeInfoModel
+import com.lagradost.models.AnimeModel
+import com.lagradost.models.NewAnimeModel
+import com.lagradost.models.SearchModel
+import com.lagradost.models.PlayerJson
+
+class AnimeONProvider : MainAPI() {
+
+ // Basic Info
+ override var mainUrl = "https://animeon.club"
+ override var name = "AnimeON"
+ override val hasMainPage = true
+ override var lang = "uk"
+ override val hasDownloadSupport = true
+ override val supportedTypes =
+ setOf(
+ TvType.Anime,
+ TvType.AnimeMovie,
+ TvType.OVA,
+ )
+
+ private val apiUrl = "$mainUrl/api/anime/"
+ private val posterApi = "$mainUrl/api/uploads/images/%s"
+ private val searchApi = "$apiUrl/search/%s?full=true"
+
+ // Sections
+ override val mainPage =
+ mainPageOf(
+ "$apiUrl/popular" to "Популярне",
+ "$apiUrl/seasons" to "Аніме поточного сезону ",
+ "$apiUrl?pageSize=24&pageIndex=%s&sortType=DESC&sort=created" to "Нове",
+ )
+
+ private val listAnimeModel = object : TypeToken>() {}.type
+
+ // Done
+ override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse {
+ if(!request.data.contains("pageIndex") && page !=1) return HomePageResponse(emptyList())
+ val document = app.get(request.data.format(page)).text
+
+ // Нове
+ if(request.data.contains("pageIndex")) {
+ val parsedJSON = Gson().fromJson(document, NewAnimeModel::class.java)
+ val homeList =
+ parsedJSON.results.map {
+ newAnimeSearchResponse(it.titleUa, "anime/${it.id}", TvType.Anime) {
+ this.posterUrl = posterApi.format(it.poster)
+ }
+ }
+ // Log.d("CakesTwix-Debug", "$cdnUrl${parsedJSON.data[1].posterId}")
+ return newHomePageResponse(request.name, homeList)
+ } else {
+ val parsedJSON = Gson().fromJson>(document, listAnimeModel)
+ val homeList =
+ parsedJSON.map {
+ newAnimeSearchResponse(it.titleUa, "anime/${it.id}", TvType.Anime) {
+ this.posterUrl = posterApi.format(it.poster)
+ }
+ }
+ // Log.d("CakesTwix-Debug", "$cdnUrl${parsedJSON.data[1].posterId}")
+ return newHomePageResponse(request.name, homeList)
+ }
+ }
+
+ override suspend fun search(query: String): List {
+ val animeJSON =
+ Gson().fromJson(app.get(searchApi.format(query)).text, SearchModel::class.java)
+ val findList =
+ animeJSON.result.map {
+ newAnimeSearchResponse(it.titleUa, "anime/${it.id}", TvType.Anime) {
+ this.posterUrl = posterApi.format(it.poster)
+ addDubStatus(isDub = true, it.episodes)
+ }
+ }
+ return findList
+ }
+
+ // Detailed information
+ override suspend fun load(url: String): LoadResponse {
+ val animeJSON =
+ Gson()
+ .fromJson(
+ app.get(url.replace("/anime/", "/api/anime/")).text,
+ AnimeInfoModel::class.java
+ )
+
+ val showStatus =
+ with(animeJSON.status.name) {
+ when {
+ contains("Онґоїнґ") -> ShowStatus.Ongoing
+ contains("Завершений") -> ShowStatus.Completed
+ else -> null
+ }
+ }
+
+ val tvType =
+ with(animeJSON.type.name) {
+ when {
+ contains("ТБ-Серіал") -> TvType.Anime
+ contains("OVA") -> TvType.OVA
+ contains("Спеціальний випуск") -> TvType.OVA
+ contains("ONA") -> TvType.OVA
+ contains("Фільм") -> TvType.AnimeMovie
+ else -> TvType.Anime
+ }
+ }
+
+
+ val episodes = mutableListOf()
+
+ val playerRawJson = app.get(animeJSON.player.url).document.select("script").html()
+ .substringAfterLast("file:\'")
+ .substringBefore("\',")
+
+ tryParseJson>(playerRawJson)?.map { dubs -> // Dubs
+ for (season in dubs.folder) { // Seasons
+ for (episode in season.folder) { // Episodes
+ episodes.add(
+ Episode(
+ "${season.title}, ${episode.title}, ${animeJSON.player.url}",
+ episode.title,
+ season.title.replace(" Сезон ", "").toIntOrNull(),
+ episode.title.replace("Серія ", "").toIntOrNull(),
+ episode.poster
+ )
+ )
+ }
+ }
+ }
+
+ return if (tvType == TvType.Anime || tvType == TvType.OVA) {
+ newAnimeLoadResponse(
+ animeJSON.titleUa,
+ "$mainUrl/anime/${animeJSON.id}",
+ tvType,
+ ) {
+ this.posterUrl = posterApi.format(animeJSON.poster)
+ this.engName = animeJSON.titleEn
+ this.tags = animeJSON.genres.map { it.name }
+ this.plot = animeJSON.description
+ addTrailer(animeJSON.trailer)
+ this.showStatus = showStatus
+ this.duration = extractIntFromString(animeJSON.episodeTime)
+ this.year = animeJSON.releaseDate
+ this.backgroundPosterUrl = posterApi.format(animeJSON.backgroundImage)
+ this.rating = animeJSON.rating.toString().toRatingInt()
+ addEpisodes(DubStatus.Dubbed, episodes)
+ addMalId(animeJSON.malId)
+ }
+ } else {
+ newMovieLoadResponse(animeJSON.titleUa, "$mainUrl/anime/${animeJSON.id}", tvType, "${animeJSON.titleUa}, ${animeJSON.player.url}") {
+ this.posterUrl = posterApi.format(animeJSON.poster)
+ this.tags = animeJSON.genres.map { it.name }
+ this.plot = animeJSON.description
+ addTrailer(animeJSON.trailer)
+ this.duration = extractIntFromString(animeJSON.episodeTime)
+ this.year = animeJSON.releaseDate
+ this.backgroundPosterUrl = posterApi.format(animeJSON.backgroundImage)
+ this.rating = animeJSON.rating.toString().toRatingInt()
+ addMalId(animeJSON.malId)
+ }
+ }
+ }
+
+
+ // It works when I click to view the series
+ override suspend fun loadLinks(
+ data: String, // (Serisl) [Season, Episode, Player Url] | (Film) [Title, Player Url]
+ isCasting: Boolean,
+ subtitleCallback: (SubtitleFile) -> Unit,
+ callback: (ExtractorLink) -> Unit
+ ): Boolean {
+ val dataList = data.split(", ")
+
+ // Its film, parse one m3u8
+ if(dataList.size == 2){
+ val m3u8Url = app.get(dataList[1]).document.select("script").html()
+ .substringAfterLast("file:\"")
+ .substringBefore("\",")
+ M3u8Helper.generateM3u8(
+ source = dataList[0],
+ streamUrl = m3u8Url,
+ referer = "https://tortuga.wtf/"
+ ).forEach(callback)
+
+ return true
+ }
+
+ val playerRawJson = app.get(dataList[2]).document.select("script").html()
+ .substringAfterLast("file:\'")
+ .substringBefore("\',")
+
+ tryParseJson>(playerRawJson)?.map { dubs -> // Dubs
+ for(season in dubs.folder){ // Seasons
+ if(season.title == dataList[0]){
+ for(episode in season.folder){ // Episodes
+ if(episode.title == dataList[1]){
+ // Add as source
+ M3u8Helper.generateM3u8(
+ source = dubs.title,
+ streamUrl = episode.file,
+ referer = "https://tortuga.wtf/"
+ ).forEach(callback)
+ }
+ }
+ }
+ }
+ }
+ return true
+ }
+
+ private fun extractIntFromString(string: String): Int? {
+ val value = Regex("(\\d+)").findAll(string).lastOrNull() ?: return null
+ if (value.value[0].toString() == "0") {
+ return value.value.drop(1).toIntOrNull()
+ }
+
+ return value.value.toIntOrNull()
+ }
+}
diff --git a/AnimeONProvider/src/main/kotlin/com/lagradost/AnimeONProviderPlugin.kt b/AnimeONProvider/src/main/kotlin/com/lagradost/AnimeONProviderPlugin.kt
new file mode 100644
index 0000000..2b09543
--- /dev/null
+++ b/AnimeONProvider/src/main/kotlin/com/lagradost/AnimeONProviderPlugin.kt
@@ -0,0 +1,13 @@
+package com.lagradost
+
+import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
+import com.lagradost.cloudstream3.plugins.Plugin
+import android.content.Context
+
+@CloudstreamPlugin
+class AnimeONProviderPlugin: Plugin() {
+ override fun load(context: Context) {
+ // All providers should be added in this manner. Please don't edit the providers list directly.
+ registerMainAPI(AnimeONProvider())
+ }
+}
diff --git a/AnimeONProvider/src/main/kotlin/com/lagradost/models/AnimeInfoModel.kt b/AnimeONProvider/src/main/kotlin/com/lagradost/models/AnimeInfoModel.kt
new file mode 100644
index 0000000..93c0953
--- /dev/null
+++ b/AnimeONProvider/src/main/kotlin/com/lagradost/models/AnimeInfoModel.kt
@@ -0,0 +1,104 @@
+package com.lagradost.models
+
+import com.google.gson.annotations.SerializedName
+
+class AnimeInfoModel (
+
+ @SerializedName("id") val id : Int,
+ @SerializedName("titleUa") val titleUa : String,
+ @SerializedName("titleEn") val titleEn : String,
+ @SerializedName("description") val description : String,
+ @SerializedName("releaseDate") val releaseDate : Int,
+ @SerializedName("producer") val producer : String,
+ @SerializedName("views") val views : Int,
+ @SerializedName("episodes") val episodes : Int,
+ @SerializedName("episodeTime") val episodeTime : String,
+ @SerializedName("poster") val poster : String,
+ @SerializedName("backgroundImage") val backgroundImage : String,
+ @SerializedName("episodesAired") val episodesAired : Int,
+ @SerializedName("createdAt") val createdAt : String,
+ @SerializedName("updatedAt") val updatedAt : String,
+ @SerializedName("trailer") val trailer : String,
+ @SerializedName("ashdiId") val ashdiId : String,
+ @SerializedName("malId") val malId : Int,
+ @SerializedName("pl") val pl : String,
+ @SerializedName("season") val season : Int,
+ @SerializedName("rating") val rating : Double,
+ @SerializedName("genres") val genres : List,
+ @SerializedName("tags") val tags : List,
+ @SerializedName("studio") val studio : Studio,
+ @SerializedName("status") val status : Status,
+ @SerializedName("age") val age : Age,
+ @SerializedName("fundups") val fundups : List,
+ @SerializedName("type") val type : Type,
+ @SerializedName("schedule") val schedule : Schedule,
+ @SerializedName("franchise") val franchise : Franchise,
+ @SerializedName("player") val player : Player,
+ @SerializedName("screenshots") val screenshots : List,
+ @SerializedName("rank") val rank : Int,
+ @SerializedName("votes") val votes : Int
+)
+
+data class Schedule (
+
+ @SerializedName("id") val id : Int,
+ @SerializedName("date") val name : String,
+ @SerializedName("episode") val episode : String,
+)
+
+data class Age (
+
+ @SerializedName("id") val id : Int,
+ @SerializedName("name") val name : String
+)
+
+data class Franchise (
+
+ @SerializedName("id") val id : Int,
+ @SerializedName("weight") val weight : Int
+)
+
+data class Fundups (
+
+ @SerializedName("id") val id : Int,
+ @SerializedName("name") val name : String
+)
+
+data class Player (
+
+ @SerializedName("id") val id : Int,
+ @SerializedName("url") val url : String,
+ @SerializedName("numEpisodeFrom") val numEpisodeFrom : String,
+ @SerializedName("numEpisodeTo") val numEpisodeTo : String
+)
+
+data class Screenshots (
+
+ @SerializedName("id") val id : Int,
+ @SerializedName("original") val original : String,
+ @SerializedName("preview") val preview : String
+)
+
+data class Status (
+
+ @SerializedName("id") val id : Int,
+ @SerializedName("name") val name : String
+)
+
+data class Studio (
+
+ @SerializedName("id") val id : Int,
+ @SerializedName("name") val name : String
+)
+
+data class Tags (
+
+ @SerializedName("id") val id : Int,
+ @SerializedName("name") val name : String
+)
+
+data class Type (
+
+ @SerializedName("id") val id : Int,
+ @SerializedName("name") val name : String
+)
diff --git a/AnimeONProvider/src/main/kotlin/com/lagradost/models/AnimeModel.kt b/AnimeONProvider/src/main/kotlin/com/lagradost/models/AnimeModel.kt
new file mode 100644
index 0000000..6d15660
--- /dev/null
+++ b/AnimeONProvider/src/main/kotlin/com/lagradost/models/AnimeModel.kt
@@ -0,0 +1,20 @@
+package com.lagradost.models
+
+import com.google.gson.annotations.SerializedName
+
+class AnimeModel (
+ @SerializedName("id") val id : Int,
+ @SerializedName("titleUa") val titleUa : String,
+ @SerializedName("description") val description : String,
+ @SerializedName("poster") val poster : String,
+ @SerializedName("backgroundImage") val backgroundImage : String,
+ @SerializedName("rating") val rating : Double,
+ @SerializedName("genres") val genres : List
+)
+
+data class Genres (
+
+ @SerializedName("id") val id : Int,
+ @SerializedName("name") val name : String,
+ @SerializedName("malId") val malId : Int
+)
\ No newline at end of file
diff --git a/AnimeONProvider/src/main/kotlin/com/lagradost/models/NewAnimeModel.kt b/AnimeONProvider/src/main/kotlin/com/lagradost/models/NewAnimeModel.kt
new file mode 100644
index 0000000..a312996
--- /dev/null
+++ b/AnimeONProvider/src/main/kotlin/com/lagradost/models/NewAnimeModel.kt
@@ -0,0 +1,27 @@
+package com.lagradost.models
+
+import com.google.gson.annotations.SerializedName
+
+class NewAnimeModel (
+
+ @SerializedName("results") val results : List,
+ @SerializedName("totalCount") val totalCount : Int
+)
+
+data class Results (
+
+ @SerializedName("id") val id : Int,
+ @SerializedName("titleUa") val titleUa : String,
+ @SerializedName("description") val description : String,
+ @SerializedName("releaseDate") val releaseDate : Int,
+ @SerializedName("views") val views : Int,
+ @SerializedName("episodes") val episodes : Int,
+ @SerializedName("poster") val poster : String,
+ @SerializedName("episodesAired") val episodesAired : Int,
+ @SerializedName("createdAt") val createdAt : String,
+ @SerializedName("malId") val malId : Int,
+ @SerializedName("rating") val rating : Double,
+ @SerializedName("status") val status : Status,
+ @SerializedName("type") val type : Type,
+ @SerializedName("genres") val genres : List
+)
\ No newline at end of file
diff --git a/AnimeONProvider/src/main/kotlin/com/lagradost/models/PlayerJson.kt b/AnimeONProvider/src/main/kotlin/com/lagradost/models/PlayerJson.kt
new file mode 100644
index 0000000..d0d8afe
--- /dev/null
+++ b/AnimeONProvider/src/main/kotlin/com/lagradost/models/PlayerJson.kt
@@ -0,0 +1,22 @@
+package com.lagradost.models
+
+data class PlayerJson (
+
+ val title : String,
+ val folder : List
+)
+
+data class Season (
+
+ val title : String,
+ val folder : List
+)
+
+data class Episode (
+
+ val title : String,
+ val file : String,
+ val id : String,
+ val poster : String,
+ val subtitle : String,
+)
diff --git a/AnimeONProvider/src/main/kotlin/com/lagradost/models/SearchModel.kt b/AnimeONProvider/src/main/kotlin/com/lagradost/models/SearchModel.kt
new file mode 100644
index 0000000..ad8c389
--- /dev/null
+++ b/AnimeONProvider/src/main/kotlin/com/lagradost/models/SearchModel.kt
@@ -0,0 +1,33 @@
+package com.lagradost.models
+
+import com.google.gson.annotations.SerializedName
+
+class SearchModel (
+
+ @SerializedName("result") val result : List,
+ @SerializedName("count") val count : Int
+)
+
+data class Result (
+
+ @SerializedName("id") val id : Int,
+ @SerializedName("titleUa") val titleUa : String,
+ @SerializedName("titleEn") val titleEn : String,
+ @SerializedName("description") val description : String,
+ @SerializedName("releaseDate") val releaseDate : Int,
+ @SerializedName("producer") val producer : String,
+ @SerializedName("views") val views : Int,
+ @SerializedName("episodes") val episodes : Int,
+ @SerializedName("episodeTime") val episodeTime : String,
+ @SerializedName("poster") val poster : String,
+ @SerializedName("backgroundImage") val backgroundImage : String,
+ @SerializedName("episodesAired") val episodesAired : Int,
+ @SerializedName("createdAt") val createdAt : String,
+ @SerializedName("updatedAt") val updatedAt : String,
+ @SerializedName("ashdiId") val ashdiId : String,
+ @SerializedName("malId") val malId : Int,
+ @SerializedName("season") val season : Int,
+ @SerializedName("rating") val rating : Double,
+ @SerializedName("type") val type : Type,
+ @SerializedName("status") val status : Status
+)