diff --git a/Anilibria/build.gradle.kts b/Anilibria/build.gradle.kts new file mode 100644 index 00000000..1d2d40f2 --- /dev/null +++ b/Anilibria/build.gradle.kts @@ -0,0 +1,27 @@ +// use an integer for version numbers +version = 1 + + +cloudstream { + language = "ru" + // 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", + "OVA", + "Anime", + ) + + iconUrl = "https://www.google.com/s2/favicons?domain=anilibria.tv&sz=%size%" +} \ No newline at end of file diff --git a/Anilibria/src/main/AndroidManifest.xml b/Anilibria/src/main/AndroidManifest.xml new file mode 100644 index 00000000..c98063f8 --- /dev/null +++ b/Anilibria/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/Anilibria/src/main/kotlin/com/hexated/Anilibria.kt b/Anilibria/src/main/kotlin/com/hexated/Anilibria.kt new file mode 100644 index 00000000..18135628 --- /dev/null +++ b/Anilibria/src/main/kotlin/com/hexated/Anilibria.kt @@ -0,0 +1,139 @@ +package com.hexated + +import com.fasterxml.jackson.annotation.JsonProperty +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson +import com.lagradost.cloudstream3.utils.ExtractorLink +import com.lagradost.cloudstream3.utils.getQualityFromName +import org.jsoup.Jsoup +import org.jsoup.nodes.Element + +class Anilibria : MainAPI() { + override var mainUrl = "https://anilibria.tv" + override var name = "Anilibria" + override val hasMainPage = true + override var lang = "ru" + override val hasDownloadSupport = true + + override val supportedTypes = setOf( + TvType.Anime, + TvType.AnimeMovie, + TvType.OVA + ) + + override val mainPage = mainPageOf( + "1" to "Новое", + "2" to "Популярное", + ) + + override suspend fun getMainPage( + page: Int, + request: MainPageRequest + ): HomePageResponse { + val document = app.post( + "$mainUrl/public/catalog.php", data = mapOf( + "page" to "$page", + "xpage" to "catalog", + "sort" to request.data, + "finish" to "1", + "search" to "{\"year\":\"\",\"genre\":\"\",\"season\":\"\"}" + ), headers = mapOf("X-Requested-With" to "XMLHttpRequest") + ).parsedSafe()?.table?.let { Jsoup.parse(it) } + val home = document?.select("a")?.mapNotNull { + it.toSearchResult() + } ?: throw ErrorLoadingException("Invalid json responses") + return newHomePageResponse(request.name, home) + } + + private fun Element.toSearchResult(): AnimeSearchResponse? { + val href = fixUrl(this.attr("href")) + val title = this.selectFirst("span")?.text() ?: return null + val posterUrl = fixUrlNull(this.selectFirst("img")?.attr("src")) + + return newAnimeSearchResponse(title, href, TvType.Anime) { + this.posterUrl = posterUrl + addDubStatus(isDub = true) + } + } + + override suspend fun search(query: String): List { + val document = app.post( + "$mainUrl/public/search.php", + data = mapOf("search" to query, "small" to "1"), + headers = mapOf("X-Requested-With" to "XMLHttpRequest") + ).parsedSafe()?.mes?.let { Jsoup.parse(it) } + + return document?.select("a")?.mapNotNull { + it.toSearchResult() + } ?: throw ErrorLoadingException("Invalid json responses") + } + + override suspend fun load(url: String): LoadResponse { + val document = app.get(url).document + + val title = document.selectFirst("h1.release-title")!!.text().trim() + val episodes = document.select("script").find { it.data().contains("var player =") }?.data() + ?.substringAfter("file:[")?.substringBefore("],")?.let { data -> + tryParseJson>("[$data]")?.mapNotNull { eps -> + Episode( + eps.file ?: return@mapNotNull null, + name = eps.title ?: return@mapNotNull null, + posterUrl = fixUrlNull(eps.poster), + ) + } + } + + return newAnimeLoadResponse(title, url, TvType.Anime) { + posterUrl = fixUrlNull(document.selectFirst("img#adminPoster")?.attr("src")) + this.year = + document.selectFirst("div#xreleaseInfo b:contains(Сезон:)")?.nextElementSibling() + ?.text()?.filter { it.isDigit() }?.toIntOrNull() + addEpisodes(DubStatus.Subbed, episodes) + plot = document.select("p.detail-description").text().trim() + this.tags = document.selectFirst("div#xreleaseInfo").toString().let { tag -> + Regex("Жанры:(.*\\n?.*)
").find(tag)?.groupValues?.getOrNull(1)?.split(",") + ?.map { it.trim() } + } + } + } + + override suspend fun loadLinks( + data: String, + isCasting: Boolean, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ): Boolean { + + data.split(",").map { it.trim() }.map { m3u -> + val quality = Regex("\\[([0-9]+p)]").find(m3u)?.groupValues?.getOrNull(1) + val link = m3u.removePrefix("[$quality]").trim() + callback.invoke( + ExtractorLink( + this.name, + this.name, + link, + "$mainUrl/", + getQualityFromName(quality), + true + ) + ) + } + + return true + } + + private data class Episodes( + @JsonProperty("file") val file: String? = null, + @JsonProperty("title") val title: String? = null, + @JsonProperty("poster") val poster: String? = null, + ) + + private data class Home( + @JsonProperty("table") val table: String? = null, + ) + + private data class Search( + @JsonProperty("mes") val mes: String? = null, + ) + +} \ No newline at end of file diff --git a/Anilibria/src/main/kotlin/com/hexated/AnilibriaPlugin.kt b/Anilibria/src/main/kotlin/com/hexated/AnilibriaPlugin.kt new file mode 100644 index 00000000..d813fb25 --- /dev/null +++ b/Anilibria/src/main/kotlin/com/hexated/AnilibriaPlugin.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 AnilibriaPlugin: Plugin() { + override fun load(context: Context) { + // All providers should be added in this manner. Please don't edit the providers list directly. + registerMainAPI(Anilibria()) + } +} \ No newline at end of file