Add anime providers

This commit is contained in:
Blatzar 2022-08-11 16:55:58 +02:00
parent b74b8614db
commit b3f33b368a
139 changed files with 9873 additions and 9 deletions

22
AA_BLANK/build.gradle.kts Normal file
View File

@ -0,0 +1,22 @@
// use an integer for version numbers
version = 1
cloudstream {
// All of these properties are optional, you can safely remove them
// description = "Lorem Ipsum"
// authors = listOf("Cloudburst")
/**
* Status int as the following:
* 0: Down
* 1: Ok
* 2: Slow
* 3: Beta only
* */
status = 1 // will be 3 if unspecified
// Set to true to get an 18+ symbol next to the plugin
adult = false // will be false if unspecified
}

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.lagradost"/>

View File

@ -0,0 +1,22 @@
// use an integer for version numbers
version = 1
cloudstream {
// All of these properties are optional, you can safely remove them
// description = "Lorem Ipsum"
// authors = listOf("Cloudburst")
/**
* Status int as the following:
* 0: Down
* 1: Ok
* 2: Slow
* 3: Beta only
* */
status = 1 // will be 3 if unspecified
// Set to true to get an 18+ symbol next to the plugin
adult = false // will be false if unspecified
}

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.lagradost"/>

View File

@ -0,0 +1,404 @@
package com.lagradost
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.LoadResponse.Companion.addActors
import com.lagradost.cloudstream3.mvvm.safeApiCall
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.M3u8Helper
import com.lagradost.cloudstream3.utils.Qualities
import org.jsoup.Jsoup
import org.mozilla.javascript.Context
import org.mozilla.javascript.Scriptable
import java.net.URI
import java.net.URLDecoder
class AllAnimeProvider : MainAPI() {
override var mainUrl = "https://allanime.site"
override var name = "AllAnime"
override val hasQuickSearch = false
override val hasMainPage = true
private fun getStatus(t: String): ShowStatus {
return when (t) {
"Finished" -> ShowStatus.Completed
"Releasing" -> ShowStatus.Ongoing
else -> ShowStatus.Completed
}
}
override val supportedTypes = setOf(TvType.Anime, TvType.AnimeMovie)
private data class Data(
@JsonProperty("shows") val shows: Shows
)
private data class Shows(
@JsonProperty("pageInfo") val pageInfo: PageInfo,
@JsonProperty("edges") val edges: List<Edges>,
@JsonProperty("__typename") val _typename: String
)
private data class Edges(
@JsonProperty("_id") val Id: String?,
@JsonProperty("name") val name: String,
@JsonProperty("englishName") val englishName: String?,
@JsonProperty("nativeName") val nativeName: String?,
@JsonProperty("thumbnail") val thumbnail: String?,
@JsonProperty("type") val type: String?,
@JsonProperty("season") val season: Season?,
@JsonProperty("score") val score: Double?,
@JsonProperty("airedStart") val airedStart: AiredStart?,
@JsonProperty("availableEpisodes") val availableEpisodes: AvailableEpisodes?,
@JsonProperty("availableEpisodesDetail") val availableEpisodesDetail: AvailableEpisodesDetail?,
@JsonProperty("studios") val studios: List<String>?,
@JsonProperty("description") val description: String?,
@JsonProperty("status") val status: String?,
)
private data class AvailableEpisodes(
@JsonProperty("sub") val sub: Int,
@JsonProperty("dub") val dub: Int,
@JsonProperty("raw") val raw: Int
)
private data class AiredStart(
@JsonProperty("year") val year: Int,
@JsonProperty("month") val month: Int,
@JsonProperty("date") val date: Int
)
private data class Season(
@JsonProperty("quarter") val quarter: String,
@JsonProperty("year") val year: Int
)
private data class PageInfo(
@JsonProperty("total") val total: Int,
@JsonProperty("__typename") val _typename: String
)
private data class AllAnimeQuery(
@JsonProperty("data") val data: Data
)
data class RandomMain(
@JsonProperty("data") var data: DataRan? = DataRan()
)
data class DataRan(
@JsonProperty("queryRandomRecommendation") var queryRandomRecommendation: ArrayList<QueryRandomRecommendation> = arrayListOf()
)
data class QueryRandomRecommendation(
@JsonProperty("_id") val Id: String? = null,
@JsonProperty("name") val name: String? = null,
@JsonProperty("englishName") val englishName: String? = null,
@JsonProperty("nativeName") val nativeName: String? = null,
@JsonProperty("thumbnail") val thumbnail: String? = null,
@JsonProperty("airedStart") val airedStart: String? = null,
@JsonProperty("availableChapters") val availableChapters: String? = null,
@JsonProperty("availableEpisodes") val availableEpisodes: String? = null,
@JsonProperty("__typename") val _typename: String? = null
)
override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse {
val items = ArrayList<HomePageList>()
val urls = listOf(
// Pair(
// "Top Anime",
// """$mainUrl/graphql?variables={"type":"anime","size":30,"dateRange":30}&extensions={"persistedQuery":{"version":1,"sha256Hash":"276d52ba09ca48ce2b8beb3affb26d9d673b22f9d1fd4892aaa39524128bc745"}}"""
// ),
// "countryOrigin":"JP" for Japanese only
Pair(
"Recently updated",
"""$mainUrl/graphql?variables={"search":{"allowAdult":false,"allowUnknown":false},"limit":30,"page":1,"translationType":"dub","countryOrigin":"ALL"}&extensions={"persistedQuery":{"version":1,"sha256Hash":"d2670e3e27ee109630991152c8484fce5ff5e280c523378001f9a23dc1839068"}}"""
),
)
val random =
"""$mainUrl/graphql?variables={"format":"anime"}&extensions={"persistedQuery":{"version":1,"sha256Hash":"21ac672633498a3698e8f6a93ce6c2b3722b29a216dcca93363bf012c360cd54"}}"""
val ranlink = app.get(random).text
val jsonran = parseJson<RandomMain>(ranlink)
val ranhome = jsonran.data?.queryRandomRecommendation?.map {
newAnimeSearchResponse(it.name!!, "$mainUrl/anime/${it.Id}", fix = false) {
this.posterUrl = it.thumbnail
this.otherName = it.nativeName
}
}
items.add(HomePageList("Random", ranhome!!))
urls.apmap { (HomeName, url) ->
val test = app.get(url).text
val json = parseJson<AllAnimeQuery>(test)
val home = ArrayList<SearchResponse>()
val results = json.data.shows.edges.filter {
// filtering in case there is an anime with 0 episodes available on the site.
!(it.availableEpisodes?.raw == 0 && it.availableEpisodes.sub == 0 && it.availableEpisodes.dub == 0)
}
results.map {
home.add(
newAnimeSearchResponse(it.name, "$mainUrl/anime/${it.Id}", fix = false) {
this.posterUrl = it.thumbnail
this.year = it.airedStart?.year
this.otherName = it.englishName
addDub(it.availableEpisodes?.dub)
addSub(it.availableEpisodes?.sub)
})
}
items.add(HomePageList(HomeName, home))
}
if (items.size <= 0) throw ErrorLoadingException()
return HomePageResponse(items)
}
override suspend fun search(query: String): List<SearchResponse> {
val link =
""" $mainUrl/graphql?variables={"search":{"allowAdult":false,"allowUnknown":false,"query":"$query"},"limit":26,"page":1,"translationType":"dub","countryOrigin":"ALL"}&extensions={"persistedQuery":{"version":1,"sha256Hash":"d2670e3e27ee109630991152c8484fce5ff5e280c523378001f9a23dc1839068"}}"""
var res = app.get(link).text
if (res.contains("PERSISTED_QUERY_NOT_FOUND")) {
res = app.get(link).text
if (res.contains("PERSISTED_QUERY_NOT_FOUND")) return emptyList()
}
val response = parseJson<AllAnimeQuery>(res)
val results = response.data.shows.edges.filter {
// filtering in case there is an anime with 0 episodes available on the site.
!(it.availableEpisodes?.raw == 0 && it.availableEpisodes.sub == 0 && it.availableEpisodes.dub == 0)
}
return results.map {
newAnimeSearchResponse(it.name, "$mainUrl/anime/${it.Id}", fix = false) {
this.posterUrl = it.thumbnail
this.year = it.airedStart?.year
this.otherName = it.englishName
addDub(it.availableEpisodes?.dub)
addSub(it.availableEpisodes?.sub)
}
}
}
private data class AvailableEpisodesDetail(
@JsonProperty("sub") val sub: List<String>,
@JsonProperty("dub") val dub: List<String>,
@JsonProperty("raw") val raw: List<String>
)
override suspend fun load(url: String): LoadResponse? {
val rhino = Context.enter()
rhino.initStandardObjects()
rhino.optimizationLevel = -1
val scope: Scriptable = rhino.initStandardObjects()
val html = app.get(url).text
val soup = Jsoup.parse(html)
val script = soup.select("script").firstOrNull {
it.html().contains("window.__NUXT__")
} ?: return null
val js = """
const window = {}
${script.html()}
const returnValue = JSON.stringify(window.__NUXT__.fetch[0].show)
""".trimIndent()
rhino.evaluateString(scope, js, "JavaScript", 1, null)
val jsEval = scope.get("returnValue", scope) ?: return null
val showData = parseJson<Edges>(jsEval as String)
val title = showData.name
val description = showData.description
val poster = showData.thumbnail
val episodes = showData.availableEpisodes.let {
if (it == null) return@let Pair(null, null)
Pair(if (it.sub != 0) ((1..it.sub).map { epNum ->
Episode(
"$mainUrl/anime/${showData.Id}/episodes/sub/$epNum", episode = epNum
)
}) else null, if (it.dub != 0) ((1..it.dub).map { epNum ->
Episode(
"$mainUrl/anime/${showData.Id}/episodes/dub/$epNum", episode = epNum
)
}) else null)
}
val characters = soup.select("div.character > div.card-character-box").mapNotNull {
val img = it?.selectFirst("img")?.attr("src") ?: return@mapNotNull null
val name = it.selectFirst("div > a")?.ownText() ?: return@mapNotNull null
val role = when (it.selectFirst("div > .text-secondary")?.text()?.trim()) {
"Main" -> ActorRole.Main
"Supporting" -> ActorRole.Supporting
"Background" -> ActorRole.Background
else -> null
}
Pair(Actor(name, img), role)
}
// bruh, they use graphql
//val recommendations = soup.select("#suggesction > div > div.p > .swipercard")?.mapNotNull {
// val recTitle = it?.selectFirst(".showname > a") ?: return@mapNotNull null
// val recName = recTitle.text() ?: return@mapNotNull null
// val href = fixUrlNull(recTitle.attr("href")) ?: return@mapNotNull null
// val img = it.selectFirst(".image > img").attr("src") ?: return@mapNotNull null
// AnimeSearchResponse(recName, href, this.name, TvType.Anime, img)
//}
return newAnimeLoadResponse(title, url, TvType.Anime) {
posterUrl = poster
year = showData.airedStart?.year
addEpisodes(DubStatus.Subbed, episodes.first)
addEpisodes(DubStatus.Dubbed, episodes.second)
addActors(characters)
//this.recommendations = recommendations
showStatus = getStatus(showData.status.toString())
plot = description?.replace(Regex("""<(.*?)>"""), "")
}
}
private val embedBlackList = listOf(
"https://mp4upload.com/",
"https://streamsb.net/",
"https://dood.to/",
"https://videobin.co/",
"https://ok.ru",
"https://streamlare.com",
)
private fun embedIsBlacklisted(url: String): Boolean {
embedBlackList.forEach {
if (it.javaClass.name == "kotlin.text.Regex") {
if ((it as Regex).matches(url)) {
return true
}
} else {
if (url.contains(it)) {
return true
}
}
}
return false
}
private fun String.sanitize(): String {
var out = this
listOf(Pair("\\u002F", "/")).forEach {
out = out.replace(it.first, it.second)
}
return out
}
private data class Links(
@JsonProperty("link") val link: String,
@JsonProperty("hls") val hls: Boolean?,
@JsonProperty("resolutionStr") val resolutionStr: String,
@JsonProperty("src") val src: String?
)
private data class AllAnimeVideoApiResponse(
@JsonProperty("links") val links: List<Links>
)
private data class ApiEndPoint(
@JsonProperty("episodeIframeHead") val episodeIframeHead: String
)
private suspend fun getM3u8Qualities(
m3u8Link: String,
referer: String,
qualityName: String,
): List<ExtractorLink> {
return M3u8Helper.generateM3u8(
this.name,
m3u8Link,
referer,
name = "${this.name} - $qualityName"
)
}
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
var apiEndPoint =
parseJson<ApiEndPoint>(app.get("$mainUrl/getVersion").text).episodeIframeHead
if (apiEndPoint.endsWith("/")) apiEndPoint =
apiEndPoint.slice(0 until apiEndPoint.length - 1)
val html = app.get(data).text
val sources = Regex("""sourceUrl[:=]"(.+?)"""").findAll(html).toList()
.map { URLDecoder.decode(it.destructured.component1().sanitize(), "UTF-8") }
sources.apmap {
safeApiCall {
var link = it.replace(" ", "%20")
if (URI(link).isAbsolute || link.startsWith("//")) {
if (link.startsWith("//")) link = "https:$it"
if (Regex("""streaming\.php\?""").matches(link)) {
// for now ignore
} else if (!embedIsBlacklisted(link)) {
if (URI(link).path.contains(".m3u")) {
getM3u8Qualities(link, data, URI(link).host).forEach(callback)
} else {
callback(
ExtractorLink(
"AllAnime - " + URI(link).host,
"",
link,
data,
Qualities.P1080.value,
false
)
)
}
}
} else {
link = apiEndPoint + URI(link).path + ".json?" + URI(link).query
val response = app.get(link)
if (response.code < 400) {
val links = parseJson<AllAnimeVideoApiResponse>(response.text).links
links.forEach { server ->
if (server.hls != null && server.hls) {
getM3u8Qualities(
server.link,
"$apiEndPoint/player?uri=" + (if (URI(server.link).host.isNotEmpty()) server.link else apiEndPoint + URI(
server.link
).path),
server.resolutionStr
).forEach(callback)
} else {
callback(
ExtractorLink(
"AllAnime - " + URI(server.link).host,
server.resolutionStr,
server.link,
"$apiEndPoint/player?uri=" + (if (URI(server.link).host.isNotEmpty()) server.link else apiEndPoint + URI(
server.link
).path),
Qualities.P1080.value,
false
)
)
}
}
}
}
}
}
return true
}
}

View File

@ -0,0 +1,14 @@
package com.lagradost
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
import com.lagradost.cloudstream3.plugins.Plugin
import android.content.Context
@CloudstreamPlugin
class AllAnimeProviderPlugin: Plugin() {
override fun load(context: Context) {
// All providers should be added in this manner. Please don't edit the providers list directly.
registerMainAPI(AllAnimeProvider())
}
}

View File

@ -0,0 +1,22 @@
// use an integer for version numbers
version = 1
cloudstream {
// All of these properties are optional, you can safely remove them
// description = "Lorem Ipsum"
// authors = listOf("Cloudburst")
/**
* Status int as the following:
* 0: Down
* 1: Ok
* 2: Slow
* 3: Beta only
* */
status = 1 // will be 3 if unspecified
// Set to true to get an 18+ symbol next to the plugin
adult = false // will be false if unspecified
}

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.lagradost"/>

View File

@ -0,0 +1,215 @@
package com.lagradost
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.LoadResponse.Companion.addAniListId
import com.lagradost.cloudstream3.LoadResponse.Companion.addDuration
import com.lagradost.cloudstream3.LoadResponse.Companion.addMalId
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.M3u8Helper
import com.lagradost.cloudstream3.utils.Qualities
class AniPlayProvider : MainAPI() {
override var mainUrl = "https://aniplay.it"
override var name = "AniPlay"
override var lang = "it"
override val hasMainPage = true
private val dubIdentifier = " (ITA)"
override val supportedTypes = setOf(
TvType.Anime,
TvType.AnimeMovie,
TvType.OVA
)
companion object {
fun getStatus(t: String?): ShowStatus? {
return when (t?.lowercase()) {
"completato" -> ShowStatus.Completed
"in corso" -> ShowStatus.Ongoing
else -> null // "annunciato"
}
}
fun getType(t: String?): TvType {
return when (t?.lowercase()) {
"ona" -> TvType.OVA
"movie" -> TvType.AnimeMovie
else -> TvType.Anime //"serie", "special"
}
}
}
private fun isDub(title: String): Boolean{
return title.contains(dubIdentifier)
}
data class ApiPoster(
@JsonProperty("imageFull") val posterUrl: String
)
data class ApiMainPageAnime(
@JsonProperty("animeId") val id: Int,
@JsonProperty("episodeNumber") val episode: String?,
@JsonProperty("animeTitle") val title: String,
@JsonProperty("animeType") val type: String,
@JsonProperty("fullHd") val fullHD: Boolean,
@JsonProperty("animeVerticalImages") val posters: List<ApiPoster>
)
data class ApiSearchResult(
@JsonProperty("id") val id: Int,
@JsonProperty("title") val title: String,
@JsonProperty("status") val status: String,
@JsonProperty("type") val type: String,
@JsonProperty("verticalImages") val posters: List<ApiPoster>
)
data class ApiGenres(
@JsonProperty("description") val name: String
)
data class ApiWebsite(
@JsonProperty("listWebsiteId") val websiteId: Int,
@JsonProperty("url") val url: String
)
data class ApiEpisode(
@JsonProperty("id") val id: Int,
@JsonProperty("title") val title: String?,
@JsonProperty("episodeNumber") val number: String,
)
private fun ApiEpisode.toEpisode() : Episode? {
val number = this.number.toIntOrNull() ?: return null
return Episode(
data = "$mainUrl/api/episode/${this.id}",
episode = number,
name = this.title
)
}
data class ApiSeason(
@JsonProperty("id") val id: Int,
@JsonProperty("name") val name: String
)
private suspend fun ApiSeason.toEpisodeList(url: String) : List<Episode> {
return app.get("$url/season/${this.id}").parsed<List<ApiEpisode>>().mapNotNull { it.toEpisode() }
}
data class ApiAnime(
@JsonProperty("title") val title: String,
@JsonProperty("alternativeTitle") val japTitle: String?,
@JsonProperty("episodeDuration") val duration: Int,
@JsonProperty("storyline") val plot: String,
@JsonProperty("type") val type: String,
@JsonProperty("status") val status: String,
@JsonProperty("genres") val genres: List<ApiGenres>,
@JsonProperty("verticalImages") val posters: List<ApiPoster>,
@JsonProperty("listWebsites") val websites: List<ApiWebsite>,
@JsonProperty("episodes") val episodes: List<ApiEpisode>,
@JsonProperty("seasons") val seasons: List<ApiSeason>?
)
data class ApiEpisodeUrl(
@JsonProperty("videoUrl") val url: String
)
override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse {
val response = app.get("$mainUrl/api/home/latest-episodes?page=0").parsed<List<ApiMainPageAnime>>()
val results = response.map{
val isDub = isDub(it.title)
newAnimeSearchResponse(
name = if (isDub) it.title.replace(dubIdentifier, "") else it.title,
url = "$mainUrl/api/anime/${it.id}",
type = getType(it.type),
){
addDubStatus(isDub, it.episode?.toIntOrNull())
this.posterUrl = it.posters.first().posterUrl
this.quality = if (it.fullHD) SearchQuality.HD else null
}
}
return HomePageResponse(listOf(HomePageList("Ultime uscite",results)))
}
override suspend fun search(query: String): List<SearchResponse> {
val response = app.get("$mainUrl/api/anime/advanced-search?page=0&size=36&query=$query").parsed<List<ApiSearchResult>>()
return response.map {
val isDub = isDub(it.title)
newAnimeSearchResponse(
name = if (isDub) it.title.replace(dubIdentifier, "") else it.title,
url = "$mainUrl/api/anime/${it.id}",
type = getType(it.type),
){
addDubStatus(isDub)
this.posterUrl = it.posters.first().posterUrl
}
}
}
override suspend fun load(url: String): LoadResponse {
val response = app.get(url).parsed<ApiAnime>()
val tags: List<String> = response.genres.map { it.name }
val malId: Int? = response.websites.find { it.websiteId == 1 }?.url?.removePrefix("https://myanimelist.net/anime/")?.split("/")?.first()?.toIntOrNull()
val aniListId: Int? = response.websites.find { it.websiteId == 4 }?.url?.removePrefix("https://anilist.co/anime/")?.split("/")?.first()?.toIntOrNull()
val episodes = if (response.seasons.isNullOrEmpty()) response.episodes.mapNotNull { it.toEpisode() } else response.seasons.map{ it.toEpisodeList(url) }.flatten()
val isDub = isDub(response.title)
return newAnimeLoadResponse(response.title, url, getType(response.type)) {
this.name = if (isDub) response.title.replace(dubIdentifier, "") else response.title
this.japName = response.japTitle
this.plot = response.plot
this.tags = tags
this.showStatus = getStatus(response.status)
addPoster(response.posters.first().posterUrl)
addEpisodes(if (isDub) DubStatus.Dubbed else DubStatus.Subbed, episodes)
addMalId(malId)
addAniListId(aniListId)
addDuration(response.duration.toString())
}
}
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
val episode = app.get(data).parsed<ApiEpisodeUrl>()
if(episode.url.contains(".m3u8")){
val m3u8Helper = M3u8Helper()
val streams = m3u8Helper.m3u8Generation(M3u8Helper.M3u8Stream(episode.url,Qualities.Unknown.value), false)
streams.forEach {
callback.invoke(
ExtractorLink(
name,
name,
it.streamUrl,
referer = mainUrl,
quality = it.quality ?: Qualities.Unknown.value,
isM3u8 = it.streamUrl.contains(".m3u8"))) }
return true
}
callback.invoke(
ExtractorLink(
name,
name,
episode.url,
referer = mainUrl,
quality = Qualities.Unknown.value,
isM3u8 = false,
)
)
return true
}
}

View File

@ -0,0 +1,14 @@
package com.lagradost
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
import com.lagradost.cloudstream3.plugins.Plugin
import android.content.Context
@CloudstreamPlugin
class AniPlayProviderPlugin: Plugin() {
override fun load(context: Context) {
// All providers should be added in this manner. Please don't edit the providers list directly.
registerMainAPI(AniPlayProvider())
}
}

View File

@ -0,0 +1,22 @@
// use an integer for version numbers
version = 1
cloudstream {
// All of these properties are optional, you can safely remove them
// description = "Lorem Ipsum"
// authors = listOf("Cloudburst")
/**
* Status int as the following:
* 0: Down
* 1: Ok
* 2: Slow
* 3: Beta only
* */
status = 1 // will be 3 if unspecified
// Set to true to get an 18+ symbol next to the plugin
adult = false // will be false if unspecified
}

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.lagradost"/>

View File

@ -0,0 +1,274 @@
package com.lagradost
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.LoadResponse.Companion.addAniListId
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.getQualityFromName
import java.net.URLDecoder
class AniflixProvider : MainAPI() {
override var mainUrl = "https://aniflix.pro"
override var name = "Aniflix"
override val hasMainPage = true
override val supportedTypes = setOf(
TvType.AnimeMovie,
TvType.OVA,
TvType.Anime,
)
companion object {
var token: String? = null
}
private suspend fun getToken(): String {
return token ?: run {
Regex("([^/]*)/_buildManifest\\.js").find(app.get(mainUrl).text)?.groupValues?.getOrNull(
1
)
?.also {
token = it
}
?: throw ErrorLoadingException("No token found")
}
}
private fun Anime.toSearchResponse(): SearchResponse? {
return newAnimeSearchResponse(
title?.english ?: title?.romaji ?: return null,
"$mainUrl/anime/${id ?: return null}"
) {
posterUrl = coverImage?.large ?: coverImage?.medium
}
}
override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse {
val items = ArrayList<HomePageList>()
val soup = app.get(mainUrl).document
val elements = listOf(
Pair("Trending Now", "div:nth-child(3) > div a"),
Pair("Popular", "div:nth-child(4) > div a"),
Pair("Top Rated", "div:nth-child(5) > div a"),
)
elements.map { (name, element) ->
val home = soup.select(element).map {
val href = it.attr("href")
val title = it.selectFirst("p.mt-2")!!.text()
val image = it.selectFirst("img.rounded-md[sizes]")!!.attr("src").replace("/_next/image?url=","")
.replace(Regex("\\&.*\$"),"")
val realposter = URLDecoder.decode(image, "UTF-8")
newAnimeSearchResponse(title, fixUrl(href)) {
this.posterUrl = realposter
}
}
items.add(HomePageList(name, home))
}
return HomePageResponse(items)
}
override suspend fun search(query: String): List<SearchResponse>? {
val token = getToken()
val url = "$mainUrl/_next/data/$token/search.json?keyword=$query"
val response = app.get(url)
val searchResponse =
response.parsedSafe<Search>()
?: throw ErrorLoadingException("No Media")
return searchResponse.pageProps?.searchResults?.Page?.media?.mapNotNull { media ->
media.toSearchResponse()
}
}
override suspend fun load(url: String): LoadResponse {
val token = getToken()
val id = Regex("$mainUrl/anime/([0-9]*)").find(url)?.groupValues?.getOrNull(1)
?: throw ErrorLoadingException("Error parsing link for id")
val res = app.get("https://aniflix.pro/_next/data/$token/anime/$id.json?id=$id")
.parsedSafe<AnimeResponsePage>()?.pageProps
?: throw ErrorLoadingException("Invalid Json reponse")
val isMovie = res.anime.format == "MOVIE"
return newAnimeLoadResponse(
res.anime.title?.english ?: res.anime.title?.romaji
?: throw ErrorLoadingException("Invalid title reponse"),
url, if (isMovie) TvType.AnimeMovie else TvType.Anime
) {
recommendations = res.recommended.mapNotNull { it.toSearchResponse() }
tags = res.anime.genres
posterUrl = res.anime.coverImage?.large ?: res.anime.coverImage?.medium
plot = res.anime.description
showStatus = when (res.anime.status) {
"FINISHED" -> ShowStatus.Completed
"RELEASING" -> ShowStatus.Ongoing
else -> null
}
addAniListId(id.toIntOrNull())
// subbed because they are both subbed and dubbed
if (isMovie)
addEpisodes(
DubStatus.Subbed,
listOf(newEpisode("$mainUrl/api/anime/?id=$id&episode=1"))
)
else
addEpisodes(DubStatus.Subbed, res.episodes.episodes?.nodes?.mapIndexed { index, node ->
val episodeIndex = node?.number ?: (index + 1)
//"$mainUrl/_next/data/$token/watch/$id.json?episode=${node.number ?: return@mapNotNull null}&id=$id"
newEpisode("$mainUrl/api/anime?id=$id&episode=${episodeIndex}") {
episode = episodeIndex
posterUrl = node?.thumbnail?.original?.url
name = node?.titles?.canonical
}
})
}
}
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
return app.get(data).parsed<AniLoadResponse>().let { res ->
val dubReferer = res.dub?.Referer ?: ""
res.dub?.sources?.forEach { source ->
callback(
ExtractorLink(
name,
"${source.label ?: name} (DUB)",
source.file ?: return@forEach,
dubReferer,
getQualityFromName(source.label),
source.type == "hls"
)
)
}
val subReferer = res.dub?.Referer ?: ""
res.sub?.sources?.forEach { source ->
callback(
ExtractorLink(
name,
"${source.label ?: name} (SUB)",
source.file ?: return@forEach,
subReferer,
getQualityFromName(source.label),
source.type == "hls"
)
)
}
!res.dub?.sources.isNullOrEmpty() && !res.sub?.sources.isNullOrEmpty()
}
}
data class AniLoadResponse(
@JsonProperty("sub") val sub: DubSubSource?,
@JsonProperty("dub") val dub: DubSubSource?,
@JsonProperty("episodes") val episodes: Int?
)
data class Sources(
@JsonProperty("file") val file: String?,
@JsonProperty("label") val label: String?,
@JsonProperty("type") val type: String?
)
data class DubSubSource(
@JsonProperty("Referer") var Referer: String?,
@JsonProperty("sources") var sources: ArrayList<Sources> = arrayListOf()
)
data class PageProps(
@JsonProperty("searchResults") val searchResults: SearchResults?
)
data class SearchResults(
@JsonProperty("Page") val Page: Page?
)
data class Page(
@JsonProperty("media") val media: ArrayList<Anime> = arrayListOf()
)
data class CoverImage(
@JsonProperty("color") val color: String?,
@JsonProperty("medium") val medium: String?,
@JsonProperty("large") val large: String?,
)
data class Title(
@JsonProperty("english") val english: String?,
@JsonProperty("romaji") val romaji: String?,
)
data class Search(
@JsonProperty("pageProps") val pageProps: PageProps?,
@JsonProperty("__N_SSP") val _NSSP: Boolean?
)
data class Anime(
@JsonProperty("status") val status: String?,
@JsonProperty("id") val id: Int?,
@JsonProperty("title") val title: Title?,
@JsonProperty("coverImage") val coverImage: CoverImage?,
@JsonProperty("format") val format: String?,
@JsonProperty("duration") val duration: Int?,
@JsonProperty("meanScore") val meanScore: Int?,
@JsonProperty("nextAiringEpisode") val nextAiringEpisode: String?,
@JsonProperty("bannerImage") val bannerImage: String?,
@JsonProperty("description") val description: String?,
@JsonProperty("genres") val genres: ArrayList<String>? = null,
@JsonProperty("season") val season: String?,
@JsonProperty("startDate") val startDate: StartDate?,
)
data class StartDate(
@JsonProperty("year") val year: Int?
)
data class AnimeResponsePage(
@JsonProperty("pageProps") val pageProps: AnimeResponse?,
)
data class AnimeResponse(
@JsonProperty("anime") val anime: Anime,
@JsonProperty("recommended") val recommended: ArrayList<Anime>,
@JsonProperty("episodes") val episodes: EpisodesParent,
)
data class EpisodesParent(
@JsonProperty("id") val id: String?,
@JsonProperty("season") val season: String?,
@JsonProperty("startDate") val startDate: String?,
@JsonProperty("episodeCount") val episodeCount: Int?,
@JsonProperty("episodes") val episodes: Episodes?,
)
data class Episodes(
@JsonProperty("nodes") val nodes: ArrayList<Nodes?> = arrayListOf()
)
data class Nodes(
@JsonProperty("number") val number: Int? = null,
@JsonProperty("titles") val titles: Titles?,
@JsonProperty("thumbnail") val thumbnail: Thumbnail?,
)
data class Titles(
@JsonProperty("canonical") val canonical: String?,
)
data class Original(
@JsonProperty("url") val url: String?,
)
data class Thumbnail(
@JsonProperty("original") val original: Original?,
)
}

View File

@ -0,0 +1,14 @@
package com.lagradost
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
import com.lagradost.cloudstream3.plugins.Plugin
import android.content.Context
@CloudstreamPlugin
class AniflixProviderPlugin: Plugin() {
override fun load(context: Context) {
// All providers should be added in this manner. Please don't edit the providers list directly.
registerMainAPI(AniflixProvider())
}
}

View File

@ -0,0 +1,22 @@
// use an integer for version numbers
version = 1
cloudstream {
// All of these properties are optional, you can safely remove them
// description = "Lorem Ipsum"
// authors = listOf("Cloudburst")
/**
* Status int as the following:
* 0: Down
* 1: Ok
* 2: Slow
* 3: Beta only
* */
status = 1 // will be 3 if unspecified
// Set to true to get an 18+ symbol next to the plugin
adult = false // will be false if unspecified
}

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.lagradost"/>

View File

@ -0,0 +1,119 @@
package com.lagradost
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.Qualities
import com.lagradost.cloudstream3.utils.extractorApis
import org.jsoup.Jsoup
import java.util.*
class AnimeFlickProvider : MainAPI() {
companion object {
fun getType(t: String): TvType {
return if (t.contains("OVA") || t.contains("Special")) TvType.OVA
else if (t.contains("Movie")) TvType.AnimeMovie
else TvType.Anime
}
}
override var mainUrl = "https://animeflick.net"
override var name = "AnimeFlick"
override val hasQuickSearch = false
override val hasMainPage = false
override val supportedTypes = setOf(
TvType.AnimeMovie,
TvType.Anime,
TvType.OVA
)
override suspend fun search(query: String): List<SearchResponse> {
val link = "https://animeflick.net/search.php?search=$query"
val html = app.get(link).text
val doc = Jsoup.parse(html)
return doc.select(".row.mt-2").mapNotNull {
val href = mainUrl + it.selectFirst("a")?.attr("href")
val title = it.selectFirst("h5 > a")?.text() ?: return@mapNotNull null
val poster = mainUrl + it.selectFirst("img")?.attr("src")?.replace("70x110", "225x320")
AnimeSearchResponse(
title,
href,
this.name,
getType(title),
poster,
null,
EnumSet.of(DubStatus.Subbed),
)
}
}
override suspend fun load(url: String): LoadResponse {
val html = app.get(url).text
val doc = Jsoup.parse(html)
val poster = mainUrl + doc.selectFirst("img.rounded")?.attr("src")
val title = doc.selectFirst("h2.title")!!.text()
val yearText = doc.selectFirst(".trending-year")?.text()
val year =
if (yearText != null) Regex("""(\d{4})""").find(yearText)?.destructured?.component1()
?.toIntOrNull() else null
val description = doc.selectFirst("p")?.text()
val genres = doc.select("a[href*=\"genre-\"]").map { it.text() }
val episodes = doc.select("#collapseOne .block-space > .row > div:nth-child(2)").map {
val name = it.selectFirst("a")?.text()
val link = mainUrl + it.selectFirst("a")?.attr("href")
Episode(link, name)
}.reversed()
return newAnimeLoadResponse(title, url, getType(title)) {
posterUrl = poster
this.year = year
addEpisodes(DubStatus.Subbed, episodes)
plot = description
tags = genres
}
}
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
val html = app.get(data).text
val episodeRegex = Regex("""(https://.*?\.mp4)""")
val links = episodeRegex.findAll(html).map {
it.value
}.toList()
for (link in links) {
var alreadyAdded = false
for (extractor in extractorApis) {
if (link.startsWith(extractor.mainUrl)) {
extractor.getSafeUrl(link, data, subtitleCallback, callback)
alreadyAdded = true
break
}
}
if (!alreadyAdded) {
callback(
ExtractorLink(
this.name,
"${this.name} - Auto",
link,
"",
Qualities.P1080.value
)
)
}
}
return true
}
}

View File

@ -0,0 +1,14 @@
package com.lagradost
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
import com.lagradost.cloudstream3.plugins.Plugin
import android.content.Context
@CloudstreamPlugin
class AnimeFlickProviderPlugin: Plugin() {
override fun load(context: Context) {
// All providers should be added in this manner. Please don't edit the providers list directly.
registerMainAPI(AnimeFlickProvider())
}
}

View File

@ -0,0 +1,22 @@
// use an integer for version numbers
version = 1
cloudstream {
// All of these properties are optional, you can safely remove them
// description = "Lorem Ipsum"
// authors = listOf("Cloudburst")
/**
* Status int as the following:
* 0: Down
* 1: Ok
* 2: Slow
* 3: Beta only
* */
status = 1 // will be 3 if unspecified
// Set to true to get an 18+ symbol next to the plugin
adult = false // will be false if unspecified
}

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.lagradost"/>

View File

@ -0,0 +1,192 @@
package com.lagradost
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.APIHolder.getCaptchaToken
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.loadExtractor
import com.lagradost.nicehttp.NiceResponse
import org.jsoup.Jsoup
import org.jsoup.nodes.Element
class AnimeIndoProvider : MainAPI() {
override var mainUrl = "https://animeindo.sbs"
override var name = "AnimeIndo"
override val hasMainPage = true
override var lang = "id"
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") || t.contains("Special")) TvType.OVA
else if (t.contains("Movie")) TvType.AnimeMovie
else TvType.Anime
}
fun getStatus(t: String): ShowStatus {
return when (t) {
"Finished Airing" -> ShowStatus.Completed
"Currently Airing" -> ShowStatus.Ongoing
else -> ShowStatus.Completed
}
}
private suspend fun request(url: String): NiceResponse {
val req = app.get(
url,
cookies = mapOf("recaptcha_cookie" to "#Asia/Jakarta#-420#win32#Windows#0,false,false#Google Inc. (Intel)~ANGLE (Intel, Intel(R) HD Graphics 400 Direct3D11 vs_5_0 ps_5_0)")
)
if (req.isSuccessful) {
return req
} else {
val document = app.get(url).document
val captchaKey =
document.select("script[src*=https://www.google.com/recaptcha/api.js?render=]")
.attr("src").substringAfter("render=").substringBefore("&amp")
val token = getCaptchaToken(url, captchaKey)
return app.post(
url,
data = mapOf(
"action" to "recaptcha_for_all",
"token" to "$token",
"sitekey" to captchaKey
)
)
}
}
}
override val mainPage = mainPageOf(
"$mainUrl/anime-terbaru/page/" to "Anime Terbaru",
"$mainUrl/donghua-terbaru/page/" to "Donghua Terbaru"
)
override suspend fun getMainPage(
page: Int,
request: MainPageRequest
): HomePageResponse {
val document = request(request.data + page).document
val home = document.select("div.post-show > article").mapNotNull {
it.toSearchResult()
}
return newHomePageResponse(request.name, home)
}
private fun getProperAnimeLink(uri: String): String {
return if (uri.contains("/anime/")) {
uri
} else {
var title = uri.substringAfter("$mainUrl/")
title = when {
(title.contains("-episode")) && !(title.contains("-movie")) -> Regex("(.+)-episode").find(
title
)?.groupValues?.get(1).toString()
(title.contains("-movie")) -> Regex("(.+)-movie").find(title)?.groupValues?.get(
1
).toString()
else -> title
}
"$mainUrl/anime/$title"
}
}
private fun Element.toSearchResult(): AnimeSearchResponse? {
val title = this.selectFirst("div.title")?.text()?.trim() ?: return null
val href = getProperAnimeLink(this.selectFirst("a")!!.attr("href"))
val posterUrl = this.select("img[itemprop=image]").attr("src").toString()
val type = getType(this.select("div.type").text().trim())
val epNum =
this.selectFirst("span.episode")?.ownText()?.replace(Regex("[^0-9]"), "")?.trim()
?.toIntOrNull()
return newAnimeSearchResponse(title, href, type) {
this.posterUrl = posterUrl
addSub(epNum)
}
}
override suspend fun search(query: String): List<SearchResponse> {
val link = "$mainUrl/?s=$query"
val document = request(link).document
return document.select(".site-main.relat > article").map {
val title = it.selectFirst("div.title > h2")!!.ownText().trim()
val href = it.selectFirst("a")!!.attr("href")
val posterUrl = it.selectFirst("img")!!.attr("src").toString()
val type = getType(it.select("div.type").text().trim())
newAnimeSearchResponse(title, href, type) {
this.posterUrl = posterUrl
}
}
}
override suspend fun load(url: String): LoadResponse {
val document = request(url).document
val title = document.selectFirst("h1.entry-title")?.text().toString().trim()
val poster = document.selectFirst("div.thumb > img[itemprop=image]")?.attr("src")
val tags = document.select("div.genxed > a").map { it.text() }
val type = getType(
document.selectFirst("div.info-content > div.spe > span:nth-child(6)")?.ownText()
.toString()
)
val year = Regex("\\d, ([0-9]*)").find(
document.select("div.info-content > div.spe > span:nth-child(9) > time").text()
)?.groupValues?.get(1)?.toIntOrNull()
val status = getStatus(
document.selectFirst("div.info-content > div.spe > span:nth-child(1)")!!.ownText()
.trim()
)
val description = document.select("div[itemprop=description] > p").text()
val trailer = document.selectFirst("div.player-embed iframe")?.attr("src")
val episodes = document.select("div.lstepsiode.listeps ul li").mapNotNull {
val header = it.selectFirst("span.lchx > a") ?: return@mapNotNull null
val name = header.text().trim()
val episode = header.text().trim().replace("Episode", "").trim().toIntOrNull()
val link = fixUrl(header.attr("href"))
Episode(link, name = name, episode = episode)
}.reversed()
return newAnimeLoadResponse(title, url, type) {
engName = title
posterUrl = poster
this.year = year
addEpisodes(DubStatus.Subbed, episodes)
showStatus = status
plot = description
this.tags = tags
addTrailer(trailer)
}
}
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
val document = request(data).document
document.select("div.itemleft > .mirror > option").mapNotNull {
fixUrl(Jsoup.parse(base64Decode(it.attr("value"))).select("iframe").attr("src"))
}.apmap {
if (it.startsWith("https://uservideo.xyz")) {
app.get(it, referer = "$mainUrl/").document.select("iframe").attr("src")
} else {
it
}
}.apmap {
loadExtractor(it, data, subtitleCallback, callback)
}
return true
}
}

View File

@ -0,0 +1,14 @@
package com.lagradost
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
import com.lagradost.cloudstream3.plugins.Plugin
import android.content.Context
@CloudstreamPlugin
class AnimeIndoProviderPlugin: Plugin() {
override fun load(context: Context) {
// All providers should be added in this manner. Please don't edit the providers list directly.
registerMainAPI(AnimeIndoProvider())
}
}

View File

@ -0,0 +1,22 @@
// use an integer for version numbers
version = 1
cloudstream {
// All of these properties are optional, you can safely remove them
// description = "Lorem Ipsum"
// authors = listOf("Cloudburst")
/**
* Status int as the following:
* 0: Down
* 1: Ok
* 2: Slow
* 3: Beta only
* */
status = 1 // will be 3 if unspecified
// Set to true to get an 18+ symbol next to the plugin
adult = false // will be false if unspecified
}

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.lagradost"/>

View File

@ -0,0 +1,562 @@
package com.lagradost
import com.fasterxml.jackson.annotation.JsonProperty
import com.fasterxml.jackson.module.kotlin.readValue
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.mvvm.suspendSafeApiCall
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.JsUnpacker
import com.lagradost.cloudstream3.utils.getQualityFromName
import com.lagradost.nicehttp.NiceResponse
import org.jsoup.Jsoup
import kotlin.math.pow
class AnimePaheProvider : MainAPI() {
// credit to https://github.com/justfoolingaround/animdl/tree/master/animdl/core/codebase/providers/animepahe
companion object {
const val MAIN_URL = "https://animepahe.com"
var cookies: Map<String, String> = mapOf()
private fun getType(t: String): TvType {
return if (t.contains("OVA") || t.contains("Special")) TvType.OVA
else if (t.contains("Movie")) TvType.AnimeMovie
else TvType.Anime
}
suspend fun generateSession(): Boolean {
if (cookies.isNotEmpty()) return true
return try {
val response = app.get("$MAIN_URL/")
cookies = response.cookies
true
} catch (e: Exception) {
false
}
}
val YTSM = Regex("ysmm = '([^']+)")
val KWIK_PARAMS_RE = Regex("""\("(\w+)",\d+,"(\w+)",(\d+),(\d+),\d+\)""")
val KWIK_D_URL = Regex("action=\"([^\"]+)\"")
val KWIK_D_TOKEN = Regex("value=\"([^\"]+)\"")
val YOUTUBE_VIDEO_LINK =
Regex("""(^(?:https?:)?(?://)?(?:www\.)?(?:youtu\.be/|youtube(?:-nocookie)?\.(?:[A-Za-z]{2,4}|[A-Za-z]{2,3}\.[A-Za-z]{2})/)(?:watch|embed/|vi?/)*(?:\?[\w=&]*vi?=)?[^#&?/]{11}.*${'$'})""")
}
override var mainUrl = MAIN_URL
override var name = "AnimePahe"
override val hasQuickSearch = false
override val hasMainPage = true
override val supportedTypes = setOf(
TvType.AnimeMovie,
TvType.Anime,
TvType.OVA
)
override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse {
data class Data(
@JsonProperty("id") val id: Int,
@JsonProperty("anime_id") val animeId: Int,
@JsonProperty("anime_title") val animeTitle: String,
@JsonProperty("anime_slug") val animeSlug: String,
@JsonProperty("episode") val episode: Int,
@JsonProperty("snapshot") val snapshot: String,
@JsonProperty("created_at") val createdAt: String,
@JsonProperty("anime_session") val animeSession: String,
)
data class AnimePaheLatestReleases(
@JsonProperty("total") val total: Int,
@JsonProperty("data") val data: List<Data>
)
val urls = listOf(
Pair("$mainUrl/api?m=airing&page=1", "Latest Releases"),
)
val items = ArrayList<HomePageList>()
for (i in urls) {
try {
val response = app.get(i.first).text
val episodes = parseJson<AnimePaheLatestReleases>(response).data.map {
newAnimeSearchResponse(
it.animeTitle,
"https://pahe.win/a/${it.animeId}?slug=${it.animeTitle}",
fix = false
) {
this.posterUrl = it.snapshot
addDubStatus(DubStatus.Subbed, it.episode)
}
}
items.add(HomePageList(i.second, episodes))
} catch (e: Exception) {
e.printStackTrace()
}
}
if (items.size <= 0) throw ErrorLoadingException()
return HomePageResponse(items)
}
data class AnimePaheSearchData(
@JsonProperty("id") val id: Int,
@JsonProperty("slug") val slug: String,
@JsonProperty("title") val title: String,
@JsonProperty("type") val type: String,
@JsonProperty("episodes") val episodes: Int,
@JsonProperty("status") val status: String,
@JsonProperty("season") val season: String,
@JsonProperty("year") val year: Int,
@JsonProperty("score") val score: Double,
@JsonProperty("poster") val poster: String,
@JsonProperty("session") val session: String,
@JsonProperty("relevance") val relevance: String
)
data class AnimePaheSearch(
@JsonProperty("total") val total: Int,
@JsonProperty("data") val data: List<AnimePaheSearchData>
)
private suspend fun getAnimeByIdAndTitle(title: String, animeId: Int): String? {
val url = "$mainUrl/api?m=search&l=8&q=$title"
val headers = mapOf("referer" to "$mainUrl/")
val req = app.get(url, headers = headers).text
val data = parseJson<AnimePaheSearch>(req)
for (anime in data.data) {
if (anime.id == animeId) {
return "https://animepahe.com/anime/${anime.session}"
}
}
return null
}
override suspend fun search(query: String): List<SearchResponse> {
val url = "$mainUrl/api?m=search&l=8&q=$query"
val headers = mapOf("referer" to "$mainUrl/")
val req = app.get(url, headers = headers).text
val data = parseJson<AnimePaheSearch>(req)
return data.data.map {
newAnimeSearchResponse(
it.title,
"https://pahe.win/a/${it.id}?slug=${it.title}",
fix = false
) {
this.posterUrl = it.poster
addDubStatus(DubStatus.Subbed, it.episodes)
}
}
}
private data class AnimeData(
@JsonProperty("id") val id: Int,
@JsonProperty("anime_id") val animeId: Int,
@JsonProperty("episode") val episode: Int,
@JsonProperty("title") val title: String,
@JsonProperty("snapshot") val snapshot: String,
@JsonProperty("session") val session: String,
@JsonProperty("filler") val filler: Int,
@JsonProperty("created_at") val createdAt: String
)
private data class AnimePaheAnimeData(
@JsonProperty("total") val total: Int,
@JsonProperty("per_page") val perPage: Int,
@JsonProperty("current_page") val currentPage: Int,
@JsonProperty("last_page") val lastPage: Int,
@JsonProperty("next_page_url") val nextPageUrl: String?,
@JsonProperty("prev_page_url") val prevPageUrl: String?,
@JsonProperty("from") val from: Int,
@JsonProperty("to") val to: Int,
@JsonProperty("data") val data: List<AnimeData>
)
private suspend fun generateListOfEpisodes(link: String): ArrayList<Episode> {
try {
val attrs = link.split('/')
val id = attrs[attrs.size - 1].split("?")[0]
val uri = "$mainUrl/api?m=release&id=$id&sort=episode_asc&page=1"
val headers = mapOf("referer" to "$mainUrl/")
val req = app.get(uri, headers = headers).text
val data = parseJson<AnimePaheAnimeData>(req)
val lastPage = data.lastPage
val perPage = data.perPage
val total = data.total
var ep = 1
val episodes = ArrayList<Episode>()
fun getEpisodeTitle(k: AnimeData): String {
return k.title.ifEmpty {
"Episode ${k.episode}"
}
}
if (lastPage == 1 && perPage > total) {
data.data.forEach {
episodes.add(
newEpisode("$mainUrl/api?m=links&id=${it.animeId}&session=${it.session}&p=kwik!!TRUE!!") {
addDate(it.createdAt)
this.name = getEpisodeTitle(it)
this.posterUrl = it.snapshot
}
)
}
} else {
for (page in 0 until lastPage) {
for (i in 0 until perPage) {
if (ep <= total) {
episodes.add(
Episode(
"$mainUrl/api?m=release&id=${id}&sort=episode_asc&page=${page + 1}&ep=${ep}!!FALSE!!"
)
)
++ep
}
}
}
}
return episodes
} catch (e: Exception) {
return ArrayList()
}
}
override suspend fun load(url: String): LoadResponse? {
return suspendSafeApiCall {
val regex = Regex("""a/(\d+)\?slug=(.+)""")
val (animeId, animeTitle) = regex.find(url)!!.destructured
val link = getAnimeByIdAndTitle(animeTitle, animeId.toInt())!!
val html = app.get(link).text
val doc = Jsoup.parse(html)
val japTitle = doc.selectFirst("h2.japanese")?.text()
val poster = doc.selectFirst(".anime-poster a")?.attr("href")
val tvType = doc.selectFirst("""a[href*="/anime/type/"]""")?.text()
val trailer: String? = if (html.contains("https://www.youtube.com/watch")) {
YOUTUBE_VIDEO_LINK.find(html)?.destructured?.component1()
} else {
null
}
val episodes = generateListOfEpisodes(url)
val year = Regex("""<strong>Aired:</strong>[^,]*, (\d+)""")
.find(html)!!.destructured.component1()
.toIntOrNull()
val status =
when (Regex("""<strong>Status:</strong>[^a]*a href=["']/anime/(.*?)["']""")
.find(html)!!.destructured.component1()) {
"airing" -> ShowStatus.Ongoing
"completed" -> ShowStatus.Completed
else -> null
}
val synopsis = doc.selectFirst(".anime-synopsis")?.text()
var anilistId: Int? = null
var malId: Int? = null
doc.select(".external-links > a").forEach { aTag ->
val split = aTag.attr("href").split("/")
if (aTag.attr("href").contains("anilist.co")) {
anilistId = split[split.size - 1].toIntOrNull()
} else if (aTag.attr("href").contains("myanimelist.net")) {
malId = split[split.size - 1].toIntOrNull()
}
}
newAnimeLoadResponse(animeTitle, url, getType(tvType.toString())) {
engName = animeTitle
japName = japTitle
this.posterUrl = poster
this.year = year
addEpisodes(DubStatus.Subbed, episodes)
this.showStatus = status
plot = synopsis
tags = if (!doc.select(".anime-genre > ul a").isEmpty()) {
ArrayList(doc.select(".anime-genre > ul a").map { it.text().toString() })
} else {
null
}
addMalId(malId)
addAniListId(anilistId)
addTrailer(trailer)
}
}
}
private fun isNumber(s: String?): Boolean {
return s?.toIntOrNull() != null
}
private fun cookieStrToMap(cookie: String): Map<String, String> {
val cookies = mutableMapOf<String, String>()
for (string in cookie.split("; ")) {
val split = string.split("=").toMutableList()
val name = split.removeFirst().trim()
val value = if (split.size == 0) {
"true"
} else {
split.joinToString("=")
}
cookies[name] = value
}
return cookies.toMap()
}
private fun getString(content: String, s1: Int, s2: Int): String {
val characterMap = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/"
val slice2 = characterMap.slice(0 until s2)
var acc: Long = 0
for ((n, i) in content.reversed().withIndex()) {
acc += (when (isNumber("$i")) {
true -> "$i".toLong()
false -> "0".toLong()
}) * s1.toDouble().pow(n.toDouble()).toInt()
}
var k = ""
while (acc > 0) {
k = slice2[(acc % s2).toInt()] + k
acc = (acc - (acc % s2)) / s2
}
return when (k != "") {
true -> k
false -> "0"
}
}
private fun decrypt(fullString: String, key: String, v1: Int, v2: Int): String {
var r = ""
var i = 0
while (i < fullString.length) {
var s = ""
while (fullString[i] != key[v2]) {
s += fullString[i]
++i
}
var j = 0
while (j < key.length) {
s = s.replace(key[j].toString(), j.toString())
++j
}
r += (getString(s, v2, 10).toInt() - v1).toChar()
++i
}
return r
}
private fun zipGen(gen: Sequence<Pair<Int, Int>>): ArrayList<Pair<Pair<Int, Int>, Pair<Int, Int>>> {
val allItems = gen.toList().toMutableList()
val newList = ArrayList<Pair<Pair<Int, Int>, Pair<Int, Int>>>()
while (allItems.size > 1) {
newList.add(Pair(allItems[0], allItems[1]))
allItems.removeAt(0)
allItems.removeAt(0)
}
return newList
}
private fun decodeAdfly(codedKey: String): String {
var r = ""
var j = ""
for ((n, l) in codedKey.withIndex()) {
if (n % 2 != 0) {
j = l + j
} else {
r += l
}
}
val encodedUri = ((r + j).toCharArray().map { it.toString() }).toMutableList()
val numbers = sequence {
for ((i, n) in encodedUri.withIndex()) {
if (isNumber(n)) {
yield(Pair(i, n.toInt()))
}
}
}
for ((first, second) in zipGen(numbers)) {
val xor = first.second.xor(second.second)
if (xor < 10) {
encodedUri[first.first] = xor.toString()
}
}
var returnValue = String(encodedUri.joinToString("").toByteArray(), Charsets.UTF_8)
returnValue = base64Decode(returnValue)
return returnValue.slice(16..returnValue.length - 17)
}
private data class VideoQuality(
@JsonProperty("id") val id: Int?,
@JsonProperty("audio") val audio: String?,
@JsonProperty("kwik") val kwik: String?,
@JsonProperty("kwik_pahewin") val kwikPahewin: String
)
private data class AnimePaheEpisodeLoadLinks(
@JsonProperty("data") val data: List<Map<String, VideoQuality>>
)
private suspend fun bypassAdfly(adflyUri: String): String {
if (!generateSession()) {
return bypassAdfly(adflyUri)
}
var responseCode = 302
var adflyContent: NiceResponse? = null
var tries = 0
while (responseCode != 200 && tries < 20) {
adflyContent = app.get(
app.get(adflyUri, cookies = cookies, allowRedirects = false).url,
cookies = cookies,
allowRedirects = false
)
cookies = cookies + adflyContent.cookies
responseCode = adflyContent.code
++tries
}
if (tries > 19) {
throw Exception("Failed to bypass adfly.")
}
return decodeAdfly(YTSM.find(adflyContent?.text.toString())!!.destructured.component1())
}
private suspend fun getStreamUrlFromKwik(url: String?): String? {
if (url == null) return null
val response =
app.get(
url,
headers = mapOf("referer" to mainUrl),
cookies = cookies
).text
Regex("eval((.|\\n)*?)</script>").find(response)?.groupValues?.get(1)?.let { jsEval ->
JsUnpacker("eval$jsEval").unpack()?.let { unPacked ->
Regex("source=\'(.*?)\'").find(unPacked)?.groupValues?.get(1)?.let { link ->
return link
}
}
}
return null
}
private suspend fun getStreamUrlFromKwikAdfly(adflyUri: String): String {
val fContent =
app.get(
bypassAdfly(adflyUri),
headers = mapOf("referer" to "https://kwik.cx/"),
cookies = cookies
)
cookies = cookies + fContent.cookies
val (fullString, key, v1, v2) = KWIK_PARAMS_RE.find(fContent.text)!!.destructured
val decrypted = decrypt(fullString, key, v1.toInt(), v2.toInt())
val uri = KWIK_D_URL.find(decrypted)!!.destructured.component1()
val tok = KWIK_D_TOKEN.find(decrypted)!!.destructured.component1()
var content: NiceResponse? = null
var code = 419
var tries = 0
while (code != 302 && tries < 20) {
content = app.post(
uri,
allowRedirects = false,
data = mapOf("_token" to tok),
headers = mapOf("referer" to fContent.url),
cookies = fContent.cookies
)
code = content.code
++tries
}
if (tries > 19) {
throw Exception("Failed to extract the stream uri from kwik.")
}
return content?.headers?.values("location").toString()
}
private suspend fun extractVideoLinks(
episodeLink: String,
callback: (ExtractorLink) -> Unit
) {
var link = episodeLink
val headers = mapOf("referer" to "$mainUrl/")
if (link.contains("!!TRUE!!")) {
link = link.replace("!!TRUE!!", "")
} else {
val regex = """&ep=(\d+)!!FALSE!!""".toRegex()
val episodeNum = regex.find(link)?.destructured?.component1()?.toIntOrNull()
link = link.replace(regex, "")
val req = app.get(link, headers = headers).text
val jsonResponse = parseJson<AnimePaheAnimeData>(req)
val ep = ((jsonResponse.data.map {
if (it.episode == episodeNum) {
it
} else {
null
}
}).filterNotNull())[0]
link = "$mainUrl/api?m=links&id=${ep.animeId}&session=${ep.session}&p=kwik"
}
val req = app.get(link, headers = headers).text
val data = mapper.readValue<AnimePaheEpisodeLoadLinks>(req)
data.data.forEach {
it.entries.toList().apmap { quality ->
getStreamUrlFromKwik(quality.value.kwik)?.let { link ->
callback(
ExtractorLink(
"KWIK",
"KWIK - ${quality.key} [${quality.value.audio ?: "jpn"}]",
link,
"https://kwik.cx/",
getQualityFromName(quality.key),
link.contains(".m3u8")
)
)
}
}
}
}
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
extractVideoLinks(data, callback)
return true
}
}

View File

@ -0,0 +1,14 @@
package com.lagradost
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
import com.lagradost.cloudstream3.plugins.Plugin
import android.content.Context
@CloudstreamPlugin
class AnimePaheProviderPlugin: Plugin() {
override fun load(context: Context) {
// All providers should be added in this manner. Please don't edit the providers list directly.
registerMainAPI(AnimePaheProvider())
}
}

View File

@ -0,0 +1,22 @@
// use an integer for version numbers
version = 1
cloudstream {
// All of these properties are optional, you can safely remove them
// description = "Lorem Ipsum"
// authors = listOf("Cloudburst")
/**
* Status int as the following:
* 0: Down
* 1: Ok
* 2: Slow
* 3: Beta only
* */
status = 1 // will be 3 if unspecified
// Set to true to get an 18+ symbol next to the plugin
adult = false // will be false if unspecified
}

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.lagradost"/>

View File

@ -0,0 +1,191 @@
package com.lagradost
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.mvvm.safeApiCall
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.Qualities
import com.lagradost.cloudstream3.utils.loadExtractor
import com.lagradost.nicehttp.NiceResponse
import org.jsoup.Jsoup
import org.jsoup.nodes.Element
class AnimeSailProvider : MainAPI() {
override var mainUrl = "https://111.90.143.42"
override var name = "AnimeSail"
override val hasMainPage = true
override var lang = "id"
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") || t.contains("Special")) TvType.OVA
else if (t.contains("Movie")) TvType.AnimeMovie
else TvType.Anime
}
fun getStatus(t: String): ShowStatus {
return when (t) {
"Completed" -> ShowStatus.Completed
"Ongoing" -> ShowStatus.Ongoing
else -> ShowStatus.Completed
}
}
}
private suspend fun request(url: String, ref: String? = null): NiceResponse {
return app.get(
url,
headers = mapOf("Accept" to "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8"),
cookies = mapOf("_as_ipin_ct" to "ID"),
referer = ref
)
}
override val mainPage = mainPageOf(
"$mainUrl/page/" to "Episode Terbaru",
"$mainUrl/movie-terbaru/page/" to "Movie Terbaru",
"$mainUrl/genres/donghua/page/" to "Donghua"
)
override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse {
val document = request(request.data + page).document
val home = document.select("article").map {
it.toSearchResult()
}
return newHomePageResponse(request.name, home)
}
private fun getProperAnimeLink(uri: String): String {
return if (uri.contains("/anime/")) {
uri
} else {
var title = uri.substringAfter("$mainUrl/")
title = when {
(title.contains("-episode")) && !(title.contains("-movie")) -> title.substringBefore(
"-episode"
)
(title.contains("-movie")) -> title.substringBefore("-movie")
else -> title
}
"$mainUrl/anime/$title"
}
}
private fun Element.toSearchResult(): AnimeSearchResponse {
val href = getProperAnimeLink(fixUrlNull(this.selectFirst("a")?.attr("href")).toString())
val title = this.select(".tt > h2").text().trim()
val posterUrl = fixUrlNull(this.selectFirst("div.limit img")?.attr("src"))
val epNum = this.selectFirst(".tt > h2")?.text()?.let {
Regex("Episode\\s?([0-9]+)").find(it)?.groupValues?.getOrNull(1)?.toIntOrNull()
}
return newAnimeSearchResponse(title, href, TvType.Anime) {
this.posterUrl = posterUrl
addSub(epNum)
}
}
override suspend fun search(query: String): List<SearchResponse> {
val link = "$mainUrl/?s=$query"
val document = request(link).document
return document.select("div.listupd article").map {
it.toSearchResult()
}
}
override suspend fun load(url: String): LoadResponse {
val document = request(url).document
val title = document.selectFirst("h1.entry-title")?.text().toString().trim()
val type = getType(
document.select("tbody th:contains(Tipe)").next().text()
)
val episodes = document.select("ul.daftar > li").map {
val header = it.select("a").text().trim()
val name =
Regex("(Episode\\s?[0-9]+)").find(header)?.groupValues?.getOrNull(0) ?: header
val link = fixUrl(it.select("a").attr("href"))
Episode(link, name = name)
}.reversed()
return newAnimeLoadResponse(title, url, type) {
posterUrl = document.selectFirst("div.entry-content > img")?.attr("src")
this.year =
document.select("tbody th:contains(Dirilis)").next().text().trim().toIntOrNull()
addEpisodes(DubStatus.Subbed, episodes)
showStatus =
getStatus(document.select("tbody th:contains(Status)").next().text().trim())
plot = document.selectFirst("div.entry-content > p")?.text()
this.tags =
document.select("tbody th:contains(Genre)").next().select("a").map { it.text() }
}
}
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
val document = request(data).document
document.select(".mobius > .mirror > option").apmap {
safeApiCall {
val iframe = fixUrl(
Jsoup.parse(base64Decode(it.attr("data-em"))).select("iframe").attr("src")
?: throw ErrorLoadingException("No iframe found")
)
when {
iframe.startsWith("$mainUrl/utils/player/arch/") || iframe.startsWith(
"$mainUrl/utils/player/race/"
) -> request(iframe, ref = data).document.select("source").attr("src")
.let { link ->
val source =
when {
iframe.contains("/arch/") -> "Arch"
iframe.contains("/race/") -> "Race"
else -> this.name
}
val quality =
Regex("\\.([0-9]{3,4})\\.").find(link)?.groupValues?.get(1)
callback.invoke(
ExtractorLink(
source = source,
name = source,
url = link,
referer = mainUrl,
quality = quality?.toIntOrNull() ?: Qualities.Unknown.value
)
)
}
// skip for now
// iframe.startsWith("$mainUrl/utils/player/fichan/") -> ""
// iframe.startsWith("$mainUrl/utils/player/blogger/") -> ""
iframe.startsWith("$mainUrl/utils/player/framezilla/") || iframe.startsWith("https://uservideo.xyz") -> {
request(iframe, ref = data).document.select("iframe").attr("src")
.let { link ->
loadExtractor(fixUrl(link), mainUrl, subtitleCallback, callback)
}
}
else -> {
loadExtractor(iframe, mainUrl, subtitleCallback, callback)
}
}
}
}
return true
}
}

View File

@ -0,0 +1,14 @@
package com.lagradost
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
import com.lagradost.cloudstream3.plugins.Plugin
import android.content.Context
@CloudstreamPlugin
class AnimeSailProviderPlugin: Plugin() {
override fun load(context: Context) {
// All providers should be added in this manner. Please don't edit the providers list directly.
registerMainAPI(AnimeSailProvider())
}
}

View File

@ -0,0 +1,22 @@
// use an integer for version numbers
version = 1
cloudstream {
// All of these properties are optional, you can safely remove them
// description = "Lorem Ipsum"
// authors = listOf("Cloudburst")
/**
* Status int as the following:
* 0: Down
* 1: Ok
* 2: Slow
* 3: Beta only
* */
status = 1 // will be 3 if unspecified
// Set to true to get an 18+ symbol next to the plugin
adult = false // will be false if unspecified
}

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.lagradost"/>

View File

@ -0,0 +1,201 @@
package com.lagradost
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.LoadResponse.Companion.addAniListId
import com.lagradost.cloudstream3.LoadResponse.Companion.addDuration
import com.lagradost.cloudstream3.LoadResponse.Companion.addMalId
import com.lagradost.cloudstream3.LoadResponse.Companion.addRating
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.Qualities
import org.jsoup.nodes.Element
class AnimeSaturnProvider : MainAPI() {
override var mainUrl = "https://www.animesaturn.cc"
override var name = "AnimeSaturn"
override var lang = "it"
override val hasMainPage = true
override val supportedTypes = setOf(
TvType.Anime,
TvType.AnimeMovie,
TvType.OVA
)
companion object {
fun getStatus(t: String?): ShowStatus? {
return when (t?.lowercase()) {
"finito" -> ShowStatus.Completed
"in corso" -> ShowStatus.Ongoing
else -> null
}
}
}
private fun Element.toSearchResult(): AnimeSearchResponse {
var title = this.select("a.badge-archivio").first()!!.text()
var isDubbed = false
if (title.contains(" (ITA)")){
title = title.replace(" (ITA)", "")
isDubbed = true
}
val url = this.select("a.badge-archivio").first()!!.attr("href")
val posterUrl = this.select("img.locandina-archivio[src]").first()!!.attr("src")
return newAnimeSearchResponse(title, url, TvType.Anime) {
addDubStatus(isDubbed)
this.posterUrl = posterUrl
}
}
private fun Element.toEpisode(): Episode? {
var episode = this.text().split(" ")[1]
if(episode.contains(".")) return null
if(episode.contains("-"))
episode = episode.split("-")[0]
return Episode(
data = this.attr("href"),
episode = episode.toInt()
)
}
override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse {
val document = app.get(mainUrl).document
val list = ArrayList<HomePageList>()
document.select("div.container:has(span.badge-saturn)").forEach {
val tabName = it.select("span.badge-saturn").first()!!.text()
if (tabName.equals("Ultimi episodi")) return@forEach
val results = ArrayList<AnimeSearchResponse>()
it.select(".main-anime-card").forEach { card ->
var title = card.select("a[title]").first()!!.attr("title")
var isDubbed = false
if(title.contains(" (ITA)")){
title = title.replace(" (ITA)", "")
isDubbed = true
}
val posterUrl = card.select("img.new-anime").first()!!.attr("src")
val url = card.select("a").first()!!.attr("href")
results.add(newAnimeSearchResponse(title, url, TvType.Anime){
addDubStatus(isDubbed)
this.posterUrl = posterUrl
})
}
list.add(HomePageList(tabName, results))
}
return HomePageResponse(list)
}
override suspend fun search(query: String): List<SearchResponse> {
val document = app.get("$mainUrl/animelist?search=$query").document
return document.select("div.item-archivio").map {
it.toSearchResult()
}
}
override suspend fun load(url: String): LoadResponse {
val document = app.get(url).document
val title = document.select("img.cover-anime").first()!!.attr("alt")
val japTitle = document.select("div.box-trasparente-alternativo").first()!!.text()
val posterUrl = document.select("img.cover-anime[src]").first()!!.attr("src")
var malId : Int? = null
var aniListId : Int? = null
document.select("[rel=\"noopener noreferrer\"]").forEach {
if(it.attr("href").contains("myanimelist"))
malId = it.attr("href").removeSuffix("/").split('/').last().toIntOrNull()
else
aniListId = it.attr("href").removeSuffix("/").split('/').last().toIntOrNull()
}
val plot = document.select("div#shown-trama").first()?.text()
val tags = document.select("a.generi-as").map { it.text() }
val details : List<String>? = document.select("div.container:contains(Stato: )").first()?.text()?.split(" ")
var status : String? = null
var duration : String? = null
var year : String? = null
var score : String? = null
val isDubbed = document.select("div.anime-title-as").first()!!.text().contains("(ITA)")
if (!details.isNullOrEmpty()) {
details.forEach {
val index = details.indexOf(it) +1
when (it) {
"Stato:" -> status = details[index]
"episodi:" -> duration = details[index]
"uscita:" -> year = details[index + 2]
"Voto:" -> score = details[index].split("/")[0]
else -> return@forEach
}
}
}
val episodes = document.select("a.bottone-ep").mapNotNull{ it.toEpisode() }
return newAnimeLoadResponse(title, url, TvType.Anime) {
this.engName = title
this.japName = japTitle
this.year = year?.toIntOrNull()
this.plot = plot
this.tags = tags
this.showStatus = getStatus(status)
addPoster(posterUrl)
addRating(score)
addEpisodes(if (isDubbed) DubStatus.Dubbed else DubStatus.Subbed, episodes)
addMalId(malId)
addAniListId(aniListId)
addDuration(duration)
}
}
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
val page = app.get(data).document
val episodeLink = page.select("div.card-body > a[href]").find {it1 ->
it1.attr("href").contains("watch?")
}?.attr("href")
val episodePage = app.get(episodeLink!!).document
val episodeUrl: String?
var isM3U8 = false
if(episodePage.select("video.afterglow > source").isNotEmpty()) //Old player
episodeUrl = episodePage.select("video.afterglow > source").first()!!.attr("src")
else{ //New player
val script = episodePage.select("script").find {
it.toString().contains("jwplayer('player_hls').setup({")
}!!.toString()
episodeUrl = script.split(" ").find { it.contains(".m3u8") and !it.contains(".replace") }!!.replace("\"","").replace(",", "")
isM3U8 = true
}
callback.invoke(
ExtractorLink(
name,
name,
episodeUrl!!,
isM3u8 = isM3U8,
referer = "https://www.animesaturn.io/", //Some servers need the old host as referer, and the new ones accept it too
quality = Qualities.Unknown.value
)
)
return true
}
}

View File

@ -0,0 +1,14 @@
package com.lagradost
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
import com.lagradost.cloudstream3.plugins.Plugin
import android.content.Context
@CloudstreamPlugin
class AnimeSaturnProviderPlugin: Plugin() {
override fun load(context: Context) {
// All providers should be added in this manner. Please don't edit the providers list directly.
registerMainAPI(AnimeSaturnProvider())
}
}

View File

@ -0,0 +1,22 @@
// use an integer for version numbers
version = 1
cloudstream {
// All of these properties are optional, you can safely remove them
// description = "Lorem Ipsum"
// authors = listOf("Cloudburst")
/**
* Status int as the following:
* 0: Down
* 1: Ok
* 2: Slow
* 3: Beta only
* */
status = 1 // will be 3 if unspecified
// Set to true to get an 18+ symbol next to the plugin
adult = false // will be false if unspecified
}

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.lagradost"/>

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,14 @@
package com.lagradost
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
import com.lagradost.cloudstream3.plugins.Plugin
import android.content.Context
@CloudstreamPlugin
class AnimeWorldProviderPlugin: Plugin() {
override fun load(context: Context) {
// All providers should be added in this manner. Please don't edit the providers list directly.
registerMainAPI(AnimeWorldProvider())
}
}

View File

@ -0,0 +1,22 @@
// use an integer for version numbers
version = 1
cloudstream {
// All of these properties are optional, you can safely remove them
// description = "Lorem Ipsum"
// authors = listOf("Cloudburst")
/**
* Status int as the following:
* 0: Down
* 1: Ok
* 2: Slow
* 3: Beta only
* */
status = 1 // will be 3 if unspecified
// Set to true to get an 18+ symbol next to the plugin
adult = false // will be false if unspecified
}

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.lagradost"/>

View File

@ -0,0 +1,249 @@
package com.lagradost
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.Qualities
import com.lagradost.cloudstream3.utils.loadExtractor
import org.jsoup.Jsoup
import java.util.*
class AnimefenixProvider:MainAPI() {
override var mainUrl = "https://animefenix.com"
override var name = "Animefenix"
override var lang = "es"
override val hasMainPage = true
override val hasChromecastSupport = true
override val hasDownloadSupport = true
override val supportedTypes = setOf(
TvType.AnimeMovie,
TvType.OVA,
TvType.Anime,
)
fun getDubStatus(title: String): DubStatus {
return if (title.contains("Latino") || title.contains("Castellano"))
DubStatus.Dubbed
else DubStatus.Subbed
}
override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse {
val urls = listOf(
Pair("$mainUrl/", "Animes"),
Pair("$mainUrl/animes?type[]=movie&order=default", "Peliculas", ),
Pair("$mainUrl/animes?type[]=ova&order=default", "OVA's", ),
)
val items = ArrayList<HomePageList>()
items.add(
HomePageList(
"Últimos episodios",
app.get(mainUrl).document.select(".capitulos-grid div.item").map {
val title = it.selectFirst("div.overtitle")?.text()
val poster = it.selectFirst("a img")?.attr("src")
val epRegex = Regex("(-(\\d+)\$|-(\\d+)\\.(\\d+))")
val url = it.selectFirst("a")?.attr("href")?.replace(epRegex,"")
?.replace("/ver/","/")
val epNum = it.selectFirst(".is-size-7")?.text()?.replace("Episodio ","")?.toIntOrNull()
newAnimeSearchResponse(title!!, url!!) {
this.posterUrl = poster
addDubStatus(getDubStatus(title), epNum)
}
})
)
urls.apmap { (url, name) ->
val response = app.get(url)
val soup = Jsoup.parse(response.text)
val home = soup.select(".list-series article").map {
val title = it.selectFirst("h3 a")?.text()
val poster = it.selectFirst("figure img")?.attr("src")
AnimeSearchResponse(
title!!,
it.selectFirst("a")?.attr("href") ?: "",
this.name,
TvType.Anime,
poster,
null,
if (title.contains("Latino")) EnumSet.of(DubStatus.Dubbed) else EnumSet.of(DubStatus.Subbed),
)
}
items.add(HomePageList(name, home))
}
if (items.size <= 0) throw ErrorLoadingException()
return HomePageResponse(items)
}
override suspend fun search(query: String): List<SearchResponse> {
return app.get("$mainUrl/animes?q=$query").document.select(".list-series article").map {
val title = it.selectFirst("h3 a")?.text()
val href = it.selectFirst("a")?.attr("href")
val image = it.selectFirst("figure img")?.attr("src")
AnimeSearchResponse(
title!!,
href!!,
this.name,
TvType.Anime,
fixUrl(image ?: ""),
null,
if (title.contains("Latino") || title.contains("Castellano")) EnumSet.of(DubStatus.Dubbed) else EnumSet.of(
DubStatus.Subbed),
)
}
}
override suspend fun load(url: String): LoadResponse {
val doc = Jsoup.parse(app.get(url, timeout = 120).text)
val poster = doc.selectFirst(".image > img")?.attr("src")
val title = doc.selectFirst("h1.title.has-text-orange")?.text()
val description = doc.selectFirst("p.has-text-light")?.text()
val genres = doc.select(".genres a").map { it.text() }
val status = when (doc.selectFirst(".is-narrow-desktop a.button")?.text()) {
"Emisión" -> ShowStatus.Ongoing
"Finalizado" -> ShowStatus.Completed
else -> null
}
val episodes = doc.select(".anime-page__episode-list li").map {
val name = it.selectFirst("span")?.text()
val link = it.selectFirst("a")?.attr("href")
Episode(link!!, name)
}.reversed()
val type = if (doc.selectFirst("ul.has-text-light")?.text()
!!.contains("Película") && episodes.size == 1
) TvType.AnimeMovie else TvType.Anime
return newAnimeLoadResponse(title!!, url, type) {
japName = null
engName = title
posterUrl = poster
addEpisodes(DubStatus.Subbed, episodes)
plot = description
tags = genres
showStatus = status
}
}
private fun cleanStreamID(input: String): String = input.replace(Regex("player=.*&amp;code=|&"),"")
data class Amazon (
@JsonProperty("file") var file : String? = null,
@JsonProperty("type") var type : String? = null,
@JsonProperty("label") var label : String? = null
)
private fun cleanExtractor(
source: String,
name: String,
url: String,
callback: (ExtractorLink) -> Unit
): Boolean {
callback(
ExtractorLink(
source,
name,
url,
"",
Qualities.Unknown.value,
false
)
)
return true
}
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
val soup = app.get(data).document
val script = soup.selectFirst(".player-container script")?.data()
if (script!!.contains("var tabsArray =")) {
val sourcesRegex = Regex("player=.*&amp;code(.*)&")
val test = sourcesRegex.findAll(script).toList()
test.apmap {
val codestream = it.value
val links = when {
codestream.contains("player=2&amp") -> "https://embedsito.com/v/"+cleanStreamID(codestream)
codestream.contains("player=3&amp") -> "https://www.mp4upload.com/embed-"+cleanStreamID(codestream)+".html"
codestream.contains("player=6&amp") -> "https://www.yourupload.com/embed/"+cleanStreamID(codestream)
codestream.contains("player=12&amp") -> "http://ok.ru/videoembed/"+cleanStreamID(codestream)
codestream.contains("player=4&amp") -> "https://sendvid.com/"+cleanStreamID(codestream)
codestream.contains("player=9&amp") -> "AmaNormal https://www.animefenix.com/stream/amz.php?v="+cleanStreamID(codestream)
codestream.contains("player=11&amp") -> "AmazonES https://www.animefenix.com/stream/amz.php?v="+cleanStreamID(codestream)
codestream.contains("player=22&amp") -> "Fireload https://www.animefenix.com/stream/fl.php?v="+cleanStreamID(codestream)
else -> ""
}
loadExtractor(links, data, subtitleCallback, callback)
argamap({
if (links.contains("AmaNormal")) {
val doc = app.get(links.replace("AmaNormal ","")).document
doc.select("script").map { script ->
if (script.data().contains("sources: [{\"file\"")) {
val text = script.data().substringAfter("sources:").substringBefore("]").replace("[","")
val json = parseJson<Amazon>(text)
if (json.file != null) {
cleanExtractor(
"Amazon",
"Amazon ${json.label}",
json.file!!,
callback
)
}
}
}
}
if (links.contains("AmazonES")) {
val amazonES = links.replace("AmazonES ", "")
val doc = app.get("$amazonES&ext=es").document
doc.select("script").map { script ->
if (script.data().contains("sources: [{\"file\"")) {
val text = script.data().substringAfter("sources:").substringBefore("]").replace("[","")
val json = parseJson<Amazon>(text)
if (json.file != null) {
cleanExtractor(
"AmazonES",
"AmazonES ${json.label}",
json.file!!,
callback
)
}
}
}
}
if (links.contains("Fireload")) {
val doc = app.get(links.replace("Fireload ", "")).document
doc.select("script").map { script ->
if (script.data().contains("sources: [{\"file\"")) {
val text = script.data().substringAfter("sources:").substringBefore("]").replace("[","")
val json = parseJson<Amazon>(text)
val testurl = if (json.file?.contains("fireload") == true) {
app.get("https://${json.file}").text
} else null
if (testurl?.contains("error") == true) {
//
} else if (json.file?.contains("fireload") == true) {
cleanExtractor(
"Fireload",
"Fireload ${json.label}",
"https://"+json.file!!,
callback
)
}
}
}
}
})
}
}
return true
}
}

View File

@ -0,0 +1,14 @@
package com.lagradost
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
import com.lagradost.cloudstream3.plugins.Plugin
import android.content.Context
@CloudstreamPlugin
class AnimefenixProviderPlugin: Plugin() {
override fun load(context: Context) {
// All providers should be added in this manner. Please don't edit the providers list directly.
registerMainAPI(AnimefenixProvider())
}
}

View File

@ -0,0 +1,22 @@
// use an integer for version numbers
version = 1
cloudstream {
// All of these properties are optional, you can safely remove them
// description = "Lorem Ipsum"
// authors = listOf("Cloudburst")
/**
* Status int as the following:
* 0: Down
* 1: Ok
* 2: Slow
* 3: Beta only
* */
status = 1 // will be 3 if unspecified
// Set to true to get an 18+ symbol next to the plugin
adult = false // will be false if unspecified
}

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.lagradost"/>

View File

@ -0,0 +1,239 @@
package com.lagradost
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.M3u8Helper.Companion.generateM3u8
import com.lagradost.cloudstream3.utils.Qualities
import com.lagradost.cloudstream3.utils.getQualityFromName
import com.lagradost.cloudstream3.utils.loadExtractor
import java.util.*
class AnimeflvIOProvider : MainAPI() {
override var mainUrl = "https://animeflv.io" //Also scrapes from animeid.to
override var name = "Animeflv.io"
override var lang = "es"
override val hasMainPage = true
override val hasChromecastSupport = true
override val hasDownloadSupport = true
override val supportedTypes = setOf(
TvType.AnimeMovie,
TvType.OVA,
TvType.Anime,
)
override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse {
val items = ArrayList<HomePageList>()
val urls = listOf(
Pair("$mainUrl/series", "Series actualizadas"),
Pair("$mainUrl/peliculas", "Peliculas actualizadas"),
)
items.add(
HomePageList(
"Estrenos",
app.get(mainUrl).document.select("div#owl-demo-premiere-movies .pull-left").map {
val title = it.selectFirst("p")?.text() ?: ""
AnimeSearchResponse(
title,
fixUrl(it.selectFirst("a")?.attr("href") ?: ""),
this.name,
TvType.Anime,
it.selectFirst("img")?.attr("src"),
it.selectFirst("span.year").toString().toIntOrNull(),
EnumSet.of(DubStatus.Subbed),
)
})
)
urls.apmap { (url, name) ->
val soup = app.get(url).document
val home = soup.select("div.item-pelicula").map {
val title = it.selectFirst(".item-detail p")?.text() ?: ""
val poster = it.selectFirst("figure img")?.attr("src")
AnimeSearchResponse(
title,
fixUrl(it.selectFirst("a")?.attr("href") ?: ""),
this.name,
TvType.Anime,
poster,
null,
if (title.contains("Latino") || title.contains("Castellano")) EnumSet.of(
DubStatus.Dubbed
) else EnumSet.of(DubStatus.Subbed),
)
}
items.add(HomePageList(name, home))
}
if (items.size <= 0) throw ErrorLoadingException()
return HomePageResponse(items)
}
override suspend fun search(query: String): List<SearchResponse> {
val headers = mapOf(
"Host" to "animeflv.io",
"User-Agent" to USER_AGENT,
"X-Requested-With" to "XMLHttpRequest",
"DNT" to "1",
"Alt-Used" to "animeflv.io",
"Connection" to "keep-alive",
"Referer" to "https://animeflv.io",
)
val url = "$mainUrl/search.html?keyword=$query"
val document = app.get(
url,
headers = headers
).document
return document.select(".item-pelicula.pull-left").map {
val title = it.selectFirst("div.item-detail p")?.text() ?: ""
val href = fixUrl(it.selectFirst("a")?.attr("href") ?: "")
var image = it.selectFirst("figure img")?.attr("src") ?: ""
val isMovie = href.contains("/pelicula/")
if (image.contains("/static/img/picture.png")) {
image = ""
}
if (isMovie) {
MovieSearchResponse(
title,
href,
this.name,
TvType.AnimeMovie,
image,
null
)
} else {
AnimeSearchResponse(
title,
href,
this.name,
TvType.Anime,
image,
null,
EnumSet.of(DubStatus.Subbed),
)
}
}
}
override suspend fun load(url: String): LoadResponse? {
// Gets the url returned from searching.
val soup = app.get(url).document
val title = soup.selectFirst(".info-content h1")?.text()
val description = soup.selectFirst("span.sinopsis")?.text()?.trim()
val poster: String? = soup.selectFirst(".poster img")?.attr("src")
val episodes = soup.select(".item-season-episodes a").map { li ->
val href = fixUrl(li.selectFirst("a")?.attr("href") ?: "")
val name = li.selectFirst("a")?.text() ?: ""
Episode(
href, name,
)
}.reversed()
val year = Regex("(\\d*)").find(soup.select(".info-half").text())
val tvType = if (url.contains("/pelicula/")) TvType.AnimeMovie else TvType.Anime
val genre = soup.select(".content-type-a a")
.map { it?.text()?.trim().toString().replace(", ", "") }
val duration = Regex("""(\d*)""").find(
soup.select("p.info-half:nth-child(4)").text()
)
return when (tvType) {
TvType.Anime -> {
return newAnimeLoadResponse(title ?: "", url, tvType) {
japName = null
engName = title
posterUrl = poster
this.year = null
addEpisodes(DubStatus.Subbed, episodes)
plot = description
tags = genre
showStatus = null
}
}
TvType.AnimeMovie -> {
MovieLoadResponse(
title ?: "",
url,
this.name,
tvType,
url,
poster,
year.toString().toIntOrNull(),
description,
null,
genre,
duration.toString().toIntOrNull(),
)
}
else -> null
}
}
data class MainJson(
@JsonProperty("source") val source: List<Source>,
@JsonProperty("source_bk") val sourceBk: String?,
@JsonProperty("track") val track: List<String>?,
@JsonProperty("advertising") val advertising: List<String>?,
@JsonProperty("linkiframe") val linkiframe: String?
)
data class Source(
@JsonProperty("file") val file: String,
@JsonProperty("label") val label: String,
@JsonProperty("default") val default: String,
@JsonProperty("type") val type: String
)
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
app.get(data).document.select("li.tab-video").apmap {
val url = fixUrl(it.attr("data-video"))
if (url.contains("animeid")) {
val ajaxurl = url.replace("streaming.php", "ajax.php")
val ajaxurltext = app.get(ajaxurl).text
val json = parseJson<MainJson>(ajaxurltext)
json.source.forEach { source ->
if (source.file.contains("m3u8")) {
generateM3u8(
"Animeflv.io",
source.file,
"https://animeid.to",
headers = mapOf("Referer" to "https://animeid.to")
).apmap {
callback(
ExtractorLink(
"Animeflv.io",
"Animeflv.io",
it.url,
"https://animeid.to",
getQualityFromName(it.quality.toString()),
it.url.contains("m3u8")
)
)
}
} else {
callback(
ExtractorLink(
name,
"$name ${source.label}",
source.file,
"https://animeid.to",
Qualities.Unknown.value,
isM3u8 = source.file.contains("m3u8")
)
)
}
}
}
loadExtractor(url, data, subtitleCallback, callback)
}
return true
}
}

View File

@ -0,0 +1,14 @@
package com.lagradost
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
import com.lagradost.cloudstream3.plugins.Plugin
import android.content.Context
@CloudstreamPlugin
class AnimeflvIOProviderPlugin: Plugin() {
override fun load(context: Context) {
// All providers should be added in this manner. Please don't edit the providers list directly.
registerMainAPI(AnimeflvIOProvider())
}
}

View File

@ -0,0 +1,22 @@
// use an integer for version numbers
version = 1
cloudstream {
// All of these properties are optional, you can safely remove them
// description = "Lorem Ipsum"
// authors = listOf("Cloudburst")
/**
* Status int as the following:
* 0: Down
* 1: Ok
* 2: Slow
* 3: Beta only
* */
status = 1 // will be 3 if unspecified
// Set to true to get an 18+ symbol next to the plugin
adult = false // will be false if unspecified
}

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.lagradost"/>

View File

@ -0,0 +1,182 @@
package com.lagradost
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.loadExtractor
import java.util.*
class AnimeflvnetProvider : MainAPI() {
companion object {
fun getType(t: String): TvType {
return if (t.contains("OVA") || t.contains("Especial")) TvType.OVA
else if (t.contains("Película")) TvType.AnimeMovie
else TvType.Anime
}
fun getDubStatus(title: String): DubStatus {
return if (title.contains("Latino") || title.contains("Castellano"))
DubStatus.Dubbed
else DubStatus.Subbed
}
}
override var mainUrl = "https://www3.animeflv.net"
override var name = "Animeflv.net"
override var lang = "es"
override val hasMainPage = true
override val hasChromecastSupport = true
override val hasDownloadSupport = true
override val supportedTypes = setOf(
TvType.AnimeMovie,
TvType.OVA,
TvType.Anime,
)
override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse {
val urls = listOf(
Pair("$mainUrl/browse?type[]=movie&order=updated", "Películas"),
Pair("$mainUrl/browse?status[]=2&order=default", "Animes"),
Pair("$mainUrl/browse?status[]=1&order=rating", "En emision"),
)
val items = ArrayList<HomePageList>()
items.add(
HomePageList(
"Últimos episodios",
app.get(mainUrl).document.select("main.Main ul.ListEpisodios li").mapNotNull {
val title = it.selectFirst("strong.Title")?.text() ?: return@mapNotNull null
val poster = it.selectFirst("span img")?.attr("src") ?: return@mapNotNull null
val epRegex = Regex("(-(\\d+)\$)")
val url = it.selectFirst("a")?.attr("href")?.replace(epRegex, "")
?.replace("ver/", "anime/") ?: return@mapNotNull null
val epNum =
it.selectFirst("span.Capi")?.text()?.replace("Episodio ", "")?.toIntOrNull()
newAnimeSearchResponse(title, url) {
this.posterUrl = fixUrl(poster)
addDubStatus(getDubStatus(title), epNum)
}
})
)
for ((url, name) in urls) {
try {
val doc = app.get(url).document
val home = doc.select("ul.ListAnimes li article").mapNotNull {
val title = it.selectFirst("h3.Title")?.text() ?: return@mapNotNull null
val poster = it.selectFirst("figure img")?.attr("src") ?: return@mapNotNull null
newAnimeSearchResponse(
title,
fixUrl(it.selectFirst("a")?.attr("href") ?: return@mapNotNull null)
) {
this.posterUrl = fixUrl(poster)
addDubStatus(getDubStatus(title))
}
}
items.add(HomePageList(name, home))
} catch (e: Exception) {
e.printStackTrace()
}
}
if (items.size <= 0) throw ErrorLoadingException()
return HomePageResponse(items)
}
data class SearchObject(
@JsonProperty("id") val id: String,
@JsonProperty("title") val title: String,
@JsonProperty("type") val type: String,
@JsonProperty("last_id") val lastId: String,
@JsonProperty("slug") val slug: String
)
override suspend fun search(query: String): List<SearchResponse> {
val response = app.post(
"https://www3.animeflv.net/api/animes/search",
data = mapOf(Pair("value", query))
).text
val json = parseJson<List<SearchObject>>(response)
return json.map { searchr ->
val title = searchr.title
val href = "$mainUrl/anime/${searchr.slug}"
val image = "$mainUrl/uploads/animes/covers/${searchr.id}.jpg"
AnimeSearchResponse(
title,
href,
this.name,
TvType.Anime,
fixUrl(image),
null,
if (title.contains("Latino") || title.contains("Castellano")) EnumSet.of(DubStatus.Dubbed) else EnumSet.of(
DubStatus.Subbed
),
)
}
}
override suspend fun load(url: String): LoadResponse {
val doc = app.get(url).document
val episodes = ArrayList<Episode>()
val title = doc.selectFirst("h1.Title")!!.text()
val poster = doc.selectFirst("div.AnimeCover div.Image figure img")?.attr("src")!!
val description = doc.selectFirst("div.Description p")?.text()
val type = doc.selectFirst("span.Type")?.text() ?: ""
val status = when (doc.selectFirst("p.AnmStts span")?.text()) {
"En emision" -> ShowStatus.Ongoing
"Finalizado" -> ShowStatus.Completed
else -> null
}
val genre = doc.select("nav.Nvgnrs a")
.map { it?.text()?.trim().toString() }
doc.select("script").map { script ->
if (script.data().contains("var episodes = [")) {
val data = script.data().substringAfter("var episodes = [").substringBefore("];")
data.split("],").forEach {
val epNum = it.removePrefix("[").substringBefore(",")
// val epthumbid = it.removePrefix("[").substringAfter(",").substringBefore("]")
val animeid = doc.selectFirst("div.Strs.RateIt")?.attr("data-id")
val epthumb = "https://cdn.animeflv.net/screenshots/$animeid/$epNum/th_3.jpg"
val link = url.replace("/anime/", "/ver/") + "-$epNum"
episodes.add(
Episode(
link,
null,
posterUrl = epthumb,
episode = epNum.toIntOrNull()
)
)
}
}
}
return newAnimeLoadResponse(title, url, getType(type)) {
posterUrl = fixUrl(poster)
addEpisodes(DubStatus.Subbed, episodes.reversed())
showStatus = status
plot = description
tags = genre
}
}
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
app.get(data).document.select("script").apmap { script ->
if (script.data().contains("var videos = {") || script.data()
.contains("var anime_id =") || script.data().contains("server")
) {
val videos = script.data().replace("\\/", "/")
fetchUrls(videos).map {
it.replace("https://embedsb.com/e/", "https://watchsb.com/e/")
.replace("https://ok.ru", "http://ok.ru")
}.apmap {
loadExtractor(it, data, subtitleCallback, callback)
}
}
}
return true
}
}

View File

@ -0,0 +1,14 @@
package com.lagradost
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
import com.lagradost.cloudstream3.plugins.Plugin
import android.content.Context
@CloudstreamPlugin
class AnimeflvnetProviderPlugin: Plugin() {
override fun load(context: Context) {
// All providers should be added in this manner. Please don't edit the providers list directly.
registerMainAPI(AnimeflvnetProvider())
}
}

View File

@ -0,0 +1,22 @@
// use an integer for version numbers
version = 1
cloudstream {
// All of these properties are optional, you can safely remove them
// description = "Lorem Ipsum"
// authors = listOf("Cloudburst")
/**
* Status int as the following:
* 0: Down
* 1: Ok
* 2: Slow
* 3: Beta only
* */
status = 1 // will be 3 if unspecified
// Set to true to get an 18+ symbol next to the plugin
adult = false // will be false if unspecified
}

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.lagradost"/>

View File

@ -0,0 +1,131 @@
package com.lagradost
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.loadExtractor
import org.jsoup.Jsoup
import java.util.*
class AnimekisaProvider : MainAPI() {
override var mainUrl = "https://animekisa.in"
override var name = "Animekisa"
override val hasMainPage = true
override val hasChromecastSupport = true
override val hasDownloadSupport = true
override val supportedTypes = setOf(
TvType.AnimeMovie,
TvType.OVA,
TvType.Anime,
)
data class Response(
@JsonProperty("html") val html: String
)
override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse {
val urls = listOf(
Pair("$mainUrl/ajax/list/views?type=all", "All animes"),
Pair("$mainUrl/ajax/list/views?type=day", "Trending now"),
Pair("$mainUrl/ajax/list/views?type=week", "Trending by week"),
Pair("$mainUrl/ajax/list/views?type=month", "Trending by month"),
)
val items = urls.mapNotNull {
suspendSafeApiCall {
val home = Jsoup.parse(
parseJson<Response>(
app.get(
it.first
).text
).html
).select("div.flw-item").mapNotNull secondMap@ {
val title = it.selectFirst("h3.title a")?.text() ?: return@secondMap null
val link = it.selectFirst("a")?.attr("href") ?: return@secondMap null
val poster = it.selectFirst("img.lazyload")?.attr("data-src")
AnimeSearchResponse(
title,
link,
this.name,
TvType.Anime,
poster,
null,
if (title.contains("(DUB)") || title.contains("(Dub)")) EnumSet.of(
DubStatus.Dubbed
) else EnumSet.of(DubStatus.Subbed),
)
}
HomePageList(name, home)
}
}
if (items.isEmpty()) throw ErrorLoadingException()
return HomePageResponse(items)
}
override suspend fun search(query: String): List<SearchResponse> {
return app.get("$mainUrl/search/?keyword=$query").document.select("div.flw-item")
.mapNotNull {
val title = it.selectFirst("h3 a")?.text() ?: ""
val url = it.selectFirst("a.film-poster-ahref")?.attr("href")
?.replace("watch/", "anime/")?.replace(
Regex("(-episode-(\\d+)/\$|-episode-(\\d+)\$|-episode-full|-episode-.*-.(/|))"),
""
) ?: return@mapNotNull null
val poster = it.selectFirst(".film-poster img")?.attr("data-src")
AnimeSearchResponse(
title,
url,
this.name,
TvType.Anime,
poster,
null,
if (title.contains("(DUB)") || title.contains("(Dub)")) EnumSet.of(
DubStatus.Dubbed
) else EnumSet.of(DubStatus.Subbed),
)
}.toList()
}
override suspend fun load(url: String): LoadResponse {
val doc = app.get(url, timeout = 120).document
val poster = doc.selectFirst(".mb-2 img")?.attr("src")
?: doc.selectFirst("head meta[property=og:image]")?.attr("content")
val title = doc.selectFirst("h1.heading-name a")!!.text()
val description = doc.selectFirst("div.description p")?.text()?.trim()
val genres = doc.select("div.row-line a").map { it.text() }
val test = if (doc.selectFirst("div.dp-i-c-right").toString()
.contains("Airing")
) ShowStatus.Ongoing else ShowStatus.Completed
val episodes = doc.select("div.tab-content ul li.nav-item").mapNotNull {
val link = it.selectFirst("a")?.attr("href") ?: return@mapNotNull null
Episode(link)
}
val type = if (doc.selectFirst(".dp-i-stats").toString()
.contains("Movies")
) TvType.AnimeMovie else TvType.Anime
return newAnimeLoadResponse(title, url, type) {
posterUrl = poster
addEpisodes(DubStatus.Subbed, episodes)
showStatus = test
plot = description
tags = genres
}
}
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
app.get(data).document.select("#servers-list ul.nav li a").apmap {
val server = it.attr("data-embed")
loadExtractor(server, data, subtitleCallback, callback)
}
return true
}
}

View File

@ -0,0 +1,14 @@
package com.lagradost
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
import com.lagradost.cloudstream3.plugins.Plugin
import android.content.Context
@CloudstreamPlugin
class AnimekisaProviderPlugin: Plugin() {
override fun load(context: Context) {
// All providers should be added in this manner. Please don't edit the providers list directly.
registerMainAPI(AnimekisaProvider())
}
}

View File

@ -141,14 +141,14 @@ class DoramasYTProvider : MainAPI() {
val encodedurl = it.select("p").attr("data-player")
val urlDecoded = base64Decode(encodedurl)
val url = (urlDecoded).replace("https://doramasyt.com/reproductor?url=", "")
// if (url.startsWith("https://www.fembed.com")) {
// val extractor = FEmbed()
// extractor.getUrl(url).forEach { link ->
// callback.invoke(link)
// }
// } else {
if (url.startsWith("https://www.fembed.com")) {
val extractor = FEmbed()
extractor.getUrl(url).forEach { link ->
callback.invoke(link)
}
} else {
loadExtractor(url, mainUrl, subtitleCallback, callback)
// }
}
}
return true
}

View File

@ -0,0 +1,67 @@
package com.lagradost
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.AppUtils
import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.getQualityFromName
class FEmbed: XStreamCdn() {
override val name: String = "FEmbed"
override val mainUrl: String = "https://www.fembed.com"
}
open class XStreamCdn : ExtractorApi() {
override val name: String = "XStreamCdn"
override val mainUrl: String = "https://embedsito.com"
override val requiresReferer = false
open var domainUrl: String = "embedsito.com"
private data class ResponseData(
@JsonProperty("file") val file: String,
@JsonProperty("label") val label: String,
//val type: String // Mp4
)
private data class ResponseJson(
@JsonProperty("success") val success: Boolean,
@JsonProperty("data") val data: List<ResponseData>?
)
override fun getExtractorUrl(id: String): String {
return "$domainUrl/api/source/$id"
}
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink> {
val headers = mapOf(
"Referer" to url,
"User-Agent" to "Mozilla/5.0 (Windows NT 10.0; rv:78.0) Gecko/20100101 Firefox/78.0",
)
val id = url.trimEnd('/').split("/").last()
val newUrl = "https://${domainUrl}/api/source/${id}"
val extractedLinksList: MutableList<ExtractorLink> = mutableListOf()
with(app.post(newUrl, headers = headers)) {
if (this.code != 200) return listOf()
val text = this.text
if (text.isEmpty()) return listOf()
if (text == """{"success":false,"data":"Video not found or has been removed"}""") return listOf()
AppUtils.parseJson<ResponseJson?>(text)?.let {
if (it.success && it.data != null) {
it.data.forEach { data ->
extractedLinksList.add(
ExtractorLink(
name,
name = name,
data.file,
url,
getQualityFromName(data.label),
)
)
}
}
}
}
return extractedLinksList
}
}

View File

@ -0,0 +1,22 @@
// use an integer for version numbers
version = 1
cloudstream {
// All of these properties are optional, you can safely remove them
// description = "Lorem Ipsum"
// authors = listOf("Cloudburst")
/**
* Status int as the following:
* 0: Down
* 1: Ok
* 2: Slow
* 3: Beta only
* */
status = 1 // will be 3 if unspecified
// Set to true to get an 18+ symbol next to the plugin
adult = false // will be false if unspecified
}

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.lagradost"/>

View File

@ -0,0 +1,270 @@
package com.lagradost
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.APIHolder.unixTime
import com.lagradost.cloudstream3.APIHolder.unixTimeMS
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.getQualityFromName
import org.jsoup.Jsoup
import java.util.*
class DubbedAnimeProvider : MainAPI() {
override var mainUrl = "https://bestdubbedanime.com"
override var name = "DubbedAnime"
override val hasQuickSearch = true
override val hasMainPage = true
override val supportedTypes = setOf(
TvType.AnimeMovie,
TvType.Anime,
)
data class QueryEpisodeResultRoot(
@JsonProperty("result")
val result: QueryEpisodeResult,
)
data class QueryEpisodeResult(
@JsonProperty("anime") val anime: List<EpisodeInfo>,
@JsonProperty("error") val error: Boolean,
@JsonProperty("errorMSG") val errorMSG: String?,
)
data class EpisodeInfo(
@JsonProperty("serversHTML") val serversHTML: String,
@JsonProperty("title") val title: String,
@JsonProperty("preview_img") val previewImg: String?,
@JsonProperty("wideImg") val wideImg: String?,
@JsonProperty("year") val year: String?,
@JsonProperty("desc") val desc: String?,
/*
@JsonProperty("rowid") val rowid: String,
@JsonProperty("status") val status: String,
@JsonProperty("skips") val skips: String,
@JsonProperty("totalEp") val totalEp: Long,
@JsonProperty("ep") val ep: String,
@JsonProperty("NextEp") val nextEp: Long,
@JsonProperty("slug") val slug: String,
@JsonProperty("showid") val showid: String,
@JsonProperty("Epviews") val epviews: String,
@JsonProperty("TotalViews") val totalViews: String,
@JsonProperty("tags") val tags: String,*/
)
private suspend fun parseDocumentTrending(url: String): List<SearchResponse> {
val response = app.get(url).text
val document = Jsoup.parse(response)
return document.select("li > a").mapNotNull {
val href = fixUrl(it.attr("href"))
val title = it.selectFirst("> div > div.cittx")?.text() ?: return@mapNotNull null
val poster = fixUrlNull(it.selectFirst("> div > div.imghddde > img")?.attr("src"))
AnimeSearchResponse(
title,
href,
this.name,
TvType.Anime,
poster,
null,
EnumSet.of(DubStatus.Dubbed),
)
}
}
private suspend fun parseDocument(
url: String,
trimEpisode: Boolean = false
): List<SearchResponse> {
val response = app.get(url).text
val document = Jsoup.parse(response)
return document.select("a.grid__link").mapNotNull {
val href = fixUrl(it.attr("href"))
val title = it.selectFirst("> div.gridtitlek")?.text() ?: return@mapNotNull null
val poster =
fixUrl(it.selectFirst("> img.grid__img")?.attr("src") ?: return@mapNotNull null)
AnimeSearchResponse(
title,
if (trimEpisode) href.removeRange(href.lastIndexOf('/'), href.length) else href,
this.name,
TvType.Anime,
poster,
null,
EnumSet.of(DubStatus.Dubbed),
)
}
}
override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse {
val trendingUrl = "$mainUrl/xz/trending.php?_=$unixTimeMS"
val lastEpisodeUrl = "$mainUrl/xz/epgrid.php?p=1&_=$unixTimeMS"
val recentlyAddedUrl = "$mainUrl/xz/gridgrabrecent.php?p=1&_=$unixTimeMS"
//val allUrl = "$mainUrl/xz/gridgrab.php?p=1&limit=12&_=$unixTimeMS"
val listItems = listOf(
HomePageList("Trending", parseDocumentTrending(trendingUrl)),
HomePageList("Recently Added", parseDocument(recentlyAddedUrl)),
HomePageList("Recent Releases", parseDocument(lastEpisodeUrl, true)),
// HomePageList("All", parseDocument(allUrl))
)
return HomePageResponse(listItems)
}
private suspend fun getEpisode(slug: String, isMovie: Boolean): EpisodeInfo {
val url =
mainUrl + (if (isMovie) "/movies/jsonMovie" else "/xz/v3/jsonEpi") + ".php?slug=$slug&_=$unixTime"
val response = app.get(url).text
val mapped = parseJson<QueryEpisodeResultRoot>(response)
return mapped.result.anime.first()
}
private fun getIsMovie(href: String): Boolean {
return href.contains("movies/")
}
private fun getSlug(href: String): String {
return href.replace("$mainUrl/", "")
}
override suspend fun quickSearch(query: String): List<SearchResponse> {
val url = "$mainUrl/xz/searchgrid.php?p=1&limit=12&s=$query&_=$unixTime"
val response = app.get(url).text
val document = Jsoup.parse(response)
val items = document.select("div.grid__item > a")
if (items.isEmpty()) return emptyList()
return items.mapNotNull { i ->
val href = fixUrl(i.attr("href"))
val title = i.selectFirst("div.gridtitlek")?.text() ?: return@mapNotNull null
val img = fixUrlNull(i.selectFirst("img.grid__img")?.attr("src"))
if (getIsMovie(href)) {
MovieSearchResponse(
title, href, this.name, TvType.AnimeMovie, img, null
)
} else {
AnimeSearchResponse(
title,
href,
this.name,
TvType.Anime,
img,
null,
EnumSet.of(DubStatus.Dubbed),
)
}
}
}
override suspend fun search(query: String): List<SearchResponse> {
val url = "$mainUrl/search/$query"
val response = app.get(url).text
val document = Jsoup.parse(response)
val items = document.select("div.resultinner > a.resulta")
if (items.isEmpty()) return ArrayList()
return items.mapNotNull { i ->
val innerDiv = i.selectFirst("> div.result")
val href = fixUrl(i.attr("href"))
val img = fixUrl(innerDiv?.selectFirst("> div.imgkz > img")?.attr("src") ?: return@mapNotNull null)
val title = innerDiv.selectFirst("> div.titleresults")?.text() ?: return@mapNotNull null
if (getIsMovie(href)) {
MovieSearchResponse(
title, href, this.name, TvType.AnimeMovie, img, null
)
} else {
AnimeSearchResponse(
title,
href,
this.name,
TvType.Anime,
img,
null,
EnumSet.of(DubStatus.Dubbed),
)
}
}
}
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
val serversHTML = (if (data.startsWith(mainUrl)) { // CLASSIC EPISODE
val slug = getSlug(data)
getEpisode(slug, false).serversHTML
} else data).replace("\\", "")
val hls = ArrayList("hl=\"(.*?)\"".toRegex().findAll(serversHTML).map {
it.groupValues[1]
}.toList())
for (hl in hls) {
try {
val sources = app.get("$mainUrl/xz/api/playeri.php?url=$hl&_=$unixTime").text
val find = "src=\"(.*?)\".*?label=\"(.*?)\"".toRegex().find(sources)
if (find != null) {
val quality = find.groupValues[2]
callback.invoke(
ExtractorLink(
this.name,
this.name + " " + quality + if (quality.endsWith('p')) "" else 'p',
fixUrl(find.groupValues[1]),
this.mainUrl,
getQualityFromName(quality)
)
)
}
} catch (e: Exception) {
//IDK
}
}
return true
}
override suspend fun load(url: String): LoadResponse {
if (getIsMovie(url)) {
val realSlug = url.replace("movies/", "")
val episode = getEpisode(realSlug, true)
val poster = episode.previewImg ?: episode.wideImg
return MovieLoadResponse(
episode.title,
realSlug,
this.name,
TvType.AnimeMovie,
episode.serversHTML,
if (poster == null) null else fixUrl(poster),
episode.year?.toIntOrNull(),
episode.desc,
null
)
} else {
val response = app.get(url).text
val document = Jsoup.parse(response)
val title = document.selectFirst("h4")!!.text()
val descriptHeader = document.selectFirst("div.animeDescript")
val descript = descriptHeader?.selectFirst("> p")?.text()
val year = descriptHeader?.selectFirst("> div.distatsx > div.sroverd")
?.text()
?.replace("Released: ", "")
?.toIntOrNull()
val episodes = document.select("a.epibloks").map {
val epTitle = it.selectFirst("> div.inwel > span.isgrxx")?.text()
Episode(fixUrl(it.attr("href")), epTitle)
}
val img = fixUrl(document.select("div.fkimgs > img").attr("src"))
return newAnimeLoadResponse(title, url, TvType.Anime) {
posterUrl = img
this.year = year
addEpisodes(DubStatus.Dubbed, episodes)
plot = descript
}
}
}
}

View File

@ -0,0 +1,14 @@
package com.lagradost
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
import com.lagradost.cloudstream3.plugins.Plugin
import android.content.Context
@CloudstreamPlugin
class DubbedAnimeProviderPlugin: Plugin() {
override fun load(context: Context) {
// All providers should be added in this manner. Please don't edit the providers list directly.
registerMainAPI(DubbedAnimeProvider())
}
}

View File

@ -0,0 +1,22 @@
// use an integer for version numbers
version = 1
cloudstream {
// All of these properties are optional, you can safely remove them
// description = "Lorem Ipsum"
// authors = listOf("Cloudburst")
/**
* Status int as the following:
* 0: Down
* 1: Ok
* 2: Slow
* 3: Beta only
* */
status = 1 // will be 3 if unspecified
// Set to true to get an 18+ symbol next to the plugin
adult = false // will be false if unspecified
}

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.lagradost"/>

View File

@ -0,0 +1,412 @@
package com.lagradost
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
import com.lagradost.cloudstream3.mvvm.safeApiCall
import com.lagradost.cloudstream3.utils.AppUtils
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.getQualityFromName
import com.lagradost.cloudstream3.utils.loadExtractor
import org.jsoup.Jsoup
import org.jsoup.nodes.Document
import java.net.URI
import java.util.*
import javax.crypto.Cipher
import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.SecretKeySpec
class GogoanimeProvider : MainAPI() {
companion object {
fun getType(t: String): TvType {
return if (t.contains("OVA") || t.contains("Special")) TvType.OVA
else if (t.contains("Movie")) TvType.AnimeMovie
else TvType.Anime
}
fun getStatus(t: String): ShowStatus {
return when (t) {
"Completed" -> ShowStatus.Completed
"Ongoing" -> ShowStatus.Ongoing
else -> ShowStatus.Completed
}
}
/**
* @param id base64Decode(show_id) + IV
* @return the encryption key
* */
private fun getKey(id: String): String? {
return normalSafeApiCall {
id.map {
it.code.toString(16)
}.joinToString("").substring(0, 32)
}
}
val qualityRegex = Regex("(\\d+)P")
// https://github.com/saikou-app/saikou/blob/3e756bd8e876ad7a9318b17110526880525a5cd3/app/src/main/java/ani/saikou/anime/source/extractors/GogoCDN.kt#L60
// No Licence on the function
private fun cryptoHandler(
string: String,
iv: String,
secretKeyString: String,
encrypt: Boolean = true
): String {
//println("IV: $iv, Key: $secretKeyString, encrypt: $encrypt, Message: $string")
val ivParameterSpec = IvParameterSpec(iv.toByteArray())
val secretKey = SecretKeySpec(secretKeyString.toByteArray(), "AES")
val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
return if (!encrypt) {
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec)
String(cipher.doFinal(base64DecodeArray(string)))
} else {
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivParameterSpec)
base64Encode(cipher.doFinal(string.toByteArray()))
}
}
private fun String.decodeHex(): ByteArray {
check(length % 2 == 0) { "Must have an even length" }
return chunked(2)
.map { it.toInt(16).toByte() }
.toByteArray()
}
/**
* @param iframeUrl something like https://gogoplay4.com/streaming.php?id=XXXXXX
* @param mainApiName used for ExtractorLink names and source
* @param iv secret iv from site, required non-null if isUsingAdaptiveKeys is off
* @param secretKey secret key for decryption from site, required non-null if isUsingAdaptiveKeys is off
* @param secretDecryptKey secret key to decrypt the response json, required non-null if isUsingAdaptiveKeys is off
* @param isUsingAdaptiveKeys generates keys from IV and ID, see getKey()
* @param isUsingAdaptiveData generate encrypt-ajax data based on $("script[data-name='episode']")[0].dataset.value
* */
suspend fun extractVidstream(
iframeUrl: String,
mainApiName: String,
callback: (ExtractorLink) -> Unit,
iv: String?,
secretKey: String?,
secretDecryptKey: String?,
// This could be removed, but i prefer it verbose
isUsingAdaptiveKeys: Boolean,
isUsingAdaptiveData: Boolean,
// If you don't want to re-fetch the document
iframeDocument: Document? = null
) = safeApiCall {
// https://github.com/saikou-app/saikou/blob/3e756bd8e876ad7a9318b17110526880525a5cd3/app/src/main/java/ani/saikou/anime/source/extractors/GogoCDN.kt
// No Licence on the following code
// Also modified of https://github.com/jmir1/aniyomi-extensions/blob/master/src/en/gogoanime/src/eu/kanade/tachiyomi/animeextension/en/gogoanime/extractors/GogoCdnExtractor.kt
// License on the code above https://github.com/jmir1/aniyomi-extensions/blob/master/LICENSE
if ((iv == null || secretKey == null || secretDecryptKey == null) && !isUsingAdaptiveKeys)
return@safeApiCall
val id = Regex("id=([^&]+)").find(iframeUrl)!!.value.removePrefix("id=")
var document: Document? = iframeDocument
val foundIv =
iv ?: (document ?: app.get(iframeUrl).document.also { document = it })
.select("""div.wrapper[class*=container]""")
.attr("class").split("-").lastOrNull() ?: return@safeApiCall
val foundKey = secretKey ?: getKey(base64Decode(id) + foundIv) ?: return@safeApiCall
val foundDecryptKey = secretDecryptKey ?: foundKey
val uri = URI(iframeUrl)
val mainUrl = "https://" + uri.host
val encryptedId = cryptoHandler(id, foundIv, foundKey)
val encryptRequestData = if (isUsingAdaptiveData) {
// Only fetch the document if necessary
val realDocument = document ?: app.get(iframeUrl).document
val dataEncrypted =
realDocument.select("script[data-name='episode']").attr("data-value")
val headers = cryptoHandler(dataEncrypted, foundIv, foundKey, false)
"id=$encryptedId&alias=$id&" + headers.substringAfter("&")
} else {
"id=$encryptedId&alias=$id"
}
val jsonResponse =
app.get(
"$mainUrl/encrypt-ajax.php?$encryptRequestData",
headers = mapOf("X-Requested-With" to "XMLHttpRequest")
)
val dataencrypted =
jsonResponse.text.substringAfter("{\"data\":\"").substringBefore("\"}")
val datadecrypted = cryptoHandler(dataencrypted, foundIv, foundDecryptKey, false)
val sources = AppUtils.parseJson<GogoSources>(datadecrypted)
fun invokeGogoSource(
source: GogoSource,
sourceCallback: (ExtractorLink) -> Unit
) {
sourceCallback.invoke(
ExtractorLink(
mainApiName,
mainApiName,
source.file,
mainUrl,
getQualityFromName(source.label),
isM3u8 = source.type == "hls" || source.label?.contains(
"auto",
ignoreCase = true
) == true
)
)
}
sources.source?.forEach {
invokeGogoSource(it, callback)
}
sources.sourceBk?.forEach {
invokeGogoSource(it, callback)
}
}
}
override var mainUrl = "https://gogoanime.lu"
override var name = "GogoAnime"
override val hasQuickSearch = false
override val hasMainPage = true
override val supportedTypes = setOf(
TvType.AnimeMovie,
TvType.Anime,
TvType.OVA
)
val headers = mapOf(
"authority" to "ajax.gogo-load.com",
"sec-ch-ua" to "\"Google Chrome\";v=\"89\", \"Chromium\";v=\"89\", \";Not A Brand\";v=\"99\"",
"accept" to "text/html, */*; q=0.01",
"dnt" to "1",
"sec-ch-ua-mobile" to "?0",
"user-agent" to "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36",
"origin" to mainUrl,
"sec-fetch-site" to "cross-site",
"sec-fetch-mode" to "cors",
"sec-fetch-dest" to "empty",
"referer" to "$mainUrl/"
)
val parseRegex =
Regex("""<li>\s*\n.*\n.*<a\s*href=["'](.*?-episode-(\d+))["']\s*title=["'](.*?)["']>\n.*?img src="(.*?)"""")
override val mainPage = mainPageOf(
Pair("1", "Recent Release - Sub"),
Pair("2", "Recent Release - Dub"),
Pair("3", "Recent Release - Chinese"),
)
override suspend fun getMainPage(
page: Int,
request : MainPageRequest
): HomePageResponse {
val params = mapOf("page" to page.toString(), "type" to request.data)
val html = app.get(
"https://ajax.gogo-load.com/ajax/page-recent-release.html",
headers = headers,
params = params
)
val isSub = listOf(1, 3).contains(request.data.toInt())
val home = parseRegex.findAll(html.text).map {
val (link, epNum, title, poster) = it.destructured
newAnimeSearchResponse(title, link) {
this.posterUrl = poster
addDubStatus(!isSub, epNum.toIntOrNull())
}
}.toList()
return newHomePageResponse(request.name, home)
}
override suspend fun search(query: String): ArrayList<SearchResponse> {
val link = "$mainUrl/search.html?keyword=$query"
val html = app.get(link).text
val doc = Jsoup.parse(html)
val episodes = doc.select(""".last_episodes li""").mapNotNull {
AnimeSearchResponse(
it.selectFirst(".name")?.text()?.replace(" (Dub)", "") ?: return@mapNotNull null,
fixUrl(it.selectFirst(".name > a")?.attr("href") ?: return@mapNotNull null),
this.name,
TvType.Anime,
it.selectFirst("img")?.attr("src"),
it.selectFirst(".released")?.text()?.split(":")?.getOrNull(1)?.trim()
?.toIntOrNull(),
if (it.selectFirst(".name")?.text()
?.contains("Dub") == true
) EnumSet.of(DubStatus.Dubbed) else EnumSet.of(
DubStatus.Subbed
),
)
}
return ArrayList(episodes)
}
private fun getProperAnimeLink(uri: String): String {
if (uri.contains("-episode")) {
val split = uri.split("/")
val slug = split[split.size - 1].split("-episode")[0]
return "$mainUrl/category/$slug"
}
return uri
}
override suspend fun load(url: String): LoadResponse {
val link = getProperAnimeLink(url)
val episodeloadApi = "https://ajax.gogo-load.com/ajax/load-list-episode"
val doc = app.get(link).document
val animeBody = doc.selectFirst(".anime_info_body_bg")
val title = animeBody?.selectFirst("h1")!!.text()
val poster = animeBody.selectFirst("img")?.attr("src")
var description: String? = null
val genre = ArrayList<String>()
var year: Int? = null
var status: String? = null
var nativeName: String? = null
var type: String? = null
animeBody.select("p.type").forEach { pType ->
when (pType.selectFirst("span")?.text()?.trim()) {
"Plot Summary:" -> {
description = pType.text().replace("Plot Summary:", "").trim()
}
"Genre:" -> {
genre.addAll(pType.select("a").map {
it.attr("title")
})
}
"Released:" -> {
year = pType.text().replace("Released:", "").trim().toIntOrNull()
}
"Status:" -> {
status = pType.text().replace("Status:", "").trim()
}
"Other name:" -> {
nativeName = pType.text().replace("Other name:", "").trim()
}
"Type:" -> {
type = pType.text().replace("type:", "").trim()
}
}
}
val animeId = doc.selectFirst("#movie_id")!!.attr("value")
val params = mapOf("ep_start" to "0", "ep_end" to "2000", "id" to animeId)
val episodes = app.get(episodeloadApi, params = params).document.select("a").map {
Episode(
fixUrl(it.attr("href").trim()),
"Episode " + it.selectFirst(".name")?.text()?.replace("EP", "")?.trim()
)
}.reversed()
return newAnimeLoadResponse(title, link, getType(type.toString())) {
japName = nativeName
engName = title
posterUrl = poster
this.year = year
addEpisodes(DubStatus.Subbed, episodes) // TODO CHECK
plot = description
tags = genre
showStatus = getStatus(status.toString())
}
}
data class GogoSources(
@JsonProperty("source") val source: List<GogoSource>?,
@JsonProperty("sourceBk") val sourceBk: List<GogoSource>?,
//val track: List<Any?>,
//val advertising: List<Any?>,
//val linkiframe: String
)
data class GogoSource(
@JsonProperty("file") val file: String,
@JsonProperty("label") val label: String?,
@JsonProperty("type") val type: String?,
@JsonProperty("default") val default: String? = null
)
private suspend fun extractVideos(
uri: String,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
) {
val doc = app.get(uri).document
val iframe = fixUrlNull(doc.selectFirst("div.play-video > iframe")?.attr("src")) ?: return
argamap(
{
val link = iframe.replace("streaming.php", "download")
val page = app.get(link, headers = mapOf("Referer" to iframe))
page.document.select(".dowload > a").apmap {
if (it.hasAttr("download")) {
val qual = if (it.text()
.contains("HDP")
) "1080" else qualityRegex.find(it.text())?.destructured?.component1()
.toString()
callback(
ExtractorLink(
"Gogoanime",
"Gogoanime",
it.attr("href"),
page.url,
getQualityFromName(qual),
it.attr("href").contains(".m3u8")
)
)
} else {
val url = it.attr("href")
loadExtractor(url, null, subtitleCallback, callback)
}
}
}, {
val streamingResponse = app.get(iframe, headers = mapOf("Referer" to iframe))
val streamingDocument = streamingResponse.document
argamap({
streamingDocument.select(".list-server-items > .linkserver")
.forEach { element ->
val status = element.attr("data-status") ?: return@forEach
if (status != "1") return@forEach
val data = element.attr("data-video") ?: return@forEach
loadExtractor(data, streamingResponse.url, subtitleCallback, callback)
}
}, {
val iv = "3134003223491201"
val secretKey = "37911490979715163134003223491201"
val secretDecryptKey = "54674138327930866480207815084989"
extractVidstream(
iframe,
this.name,
callback,
iv,
secretKey,
secretDecryptKey,
isUsingAdaptiveKeys = false,
isUsingAdaptiveData = true
)
})
}
)
}
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
extractVideos(data, subtitleCallback, callback)
return true
}
}

View File

@ -0,0 +1,14 @@
package com.lagradost
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
import com.lagradost.cloudstream3.plugins.Plugin
import android.content.Context
@CloudstreamPlugin
class GogoanimeProviderPlugin: Plugin() {
override fun load(context: Context) {
// All providers should be added in this manner. Please don't edit the providers list directly.
registerMainAPI(GogoanimeProvider())
}
}

View File

@ -0,0 +1,22 @@
// use an integer for version numbers
version = 1
cloudstream {
// All of these properties are optional, you can safely remove them
// description = "Lorem Ipsum"
// authors = listOf("Cloudburst")
/**
* Status int as the following:
* 0: Down
* 1: Ok
* 2: Slow
* 3: Beta only
* */
status = 1 // will be 3 if unspecified
// Set to true to get an 18+ symbol next to the plugin
adult = false // will be false if unspecified
}

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.lagradost"/>

View File

@ -0,0 +1,232 @@
package com.lagradost
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
import com.lagradost.cloudstream3.mvvm.safeApiCall
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.M3u8Helper
import com.lagradost.cloudstream3.utils.Qualities
import com.lagradost.cloudstream3.utils.loadExtractor
import org.jsoup.Jsoup
class GomunimeProvider : MainAPI() {
override var mainUrl = "https://185.231.223.76"
override var name = "Gomunime"
override val hasMainPage = true
override var lang = "id"
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") || t.contains("Special")) TvType.OVA
else if (t.contains("Movie")) TvType.AnimeMovie
else TvType.Anime
}
fun getStatus(t: String): ShowStatus {
return when (t) {
"Completed" -> ShowStatus.Completed
"Ongoing" -> ShowStatus.Ongoing
else -> ShowStatus.Completed
}
}
}
override val mainPage = mainPageOf(
"e" to "Episode Baru",
"c" to "Completed",
"la" to "Live Action",
"t" to "Trending"
)
override suspend fun getMainPage(
page: Int,
request: MainPageRequest
): HomePageResponse {
val home = Jsoup.parse(
(app.post(
url = "$mainUrl/wp-admin/admin-ajax.php/wp-admin/admin-ajax.php",
headers = mapOf("Referer" to mainUrl),
data = mapOf(
"action" to "home_ajax",
"fungsi" to request.data,
"pag" to "$page"
)
).parsedSafe<Response>()?.html ?: throw ErrorLoadingException("Invalid Json reponse"))
).select("li").mapNotNull {
val title = it.selectFirst("a.name")?.text()?.trim() ?: return@mapNotNull null
val href = getProperAnimeLink(it.selectFirst("a")!!.attr("href"))
val posterUrl = it.selectFirst("img")?.attr("src")
val type = getType(it.selectFirst(".taglist > span")!!.text().trim())
val epNum = it.select(".tag.ep").text().replace(Regex("[^0-9]"), "").trim()
.toIntOrNull()
newAnimeSearchResponse(title, href, type) {
this.posterUrl = posterUrl
addSub(epNum)
}
}
return newHomePageResponse(request.name, home)
}
private fun getProperAnimeLink(uri: String): String {
return if (uri.contains("-episode")) {
val href =
"$mainUrl/anime/" + Regex("\\w\\d/(.*)-episode.*").find(uri)?.groupValues?.get(1)
.toString()
when {
href.contains("pokemon") -> href.replace(Regex("-[0-9]+"), "")
else -> href
}
} else {
uri
}
}
override suspend fun search(query: String): List<SearchResponse> {
val link = "$mainUrl/?s=$query"
val document = app.get(link).document
return document.select(".anime-list > li").map {
val title = it.selectFirst("a.name")!!.text()
val poster = it.selectFirst("img")!!.attr("src")
val tvType = getType(it.selectFirst(".taglist > span")?.text().toString())
val href = fixUrl(it.selectFirst("a.name")!!.attr("href"))
newAnimeSearchResponse(title, href, tvType) {
this.posterUrl = poster
addDubStatus(dubExist = false, subExist = true)
}
}
}
override suspend fun load(url: String): LoadResponse {
val document = app.get(url).document
val title = document.selectFirst(".entry-title")?.text().toString()
val poster = document.selectFirst(".thumbposter > img")?.attr("data-lazy-src")
val tags = document.select(".genxed > a").map { it.text() }
val year = Regex("\\d, ([0-9]*)").find(
document.select("time[itemprop = datePublished]").text()
)?.groupValues?.get(1)?.toIntOrNull()
val status = getStatus(document.selectFirst(".spe > span")!!.ownText())
val description = document.select("div[itemprop = description] > p").text()
val trailer = document.selectFirst("div.embed-responsive noscript iframe")?.attr("src")
val episodes = parseJson<List<EpisodeElement>>(
Regex("var episodelist = (\\[.*])").find(
document.select(".bixbox.bxcl.epcheck > script").toString().trim()
)?.groupValues?.get(1).toString().replace(Regex("""\\"""), "").trim()
).map {
val name =
Regex("(Episode\\s?[0-9]+)").find(it.epTitle.toString())?.groupValues?.getOrNull(0)
?: it.epTitle
val link = it.epLink
Episode(link, name)
}.reversed()
return newAnimeLoadResponse(title, url, TvType.Anime) {
engName = title
posterUrl = poster
this.year = year
addEpisodes(DubStatus.Subbed, episodes)
showStatus = status
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 scriptData = document.select("aside.sidebar > script").dataNodes().toString()
val key = scriptData.substringAfter("var a_ray = '").substringBefore("';")
val title = scriptData.substringAfter("var judul_postingan = \"").substringBefore("\";")
val sources: List<Pair<String, String>> = app.post(
url = "https://path.gomuni.me/app/vapi.php",
data = mapOf("data" to key, "judul" to title, "func" to "mirror")
).document.select("div.gomunime-server-mirror").map {
Pair(
it.attr("data-vhash"),
it.attr("data-type")
)
}
sources.apmap {
safeApiCall {
when {
it.second.contains("frame") -> {
loadExtractor(it.first, data, subtitleCallback, callback)
}
it.second.contains("hls") -> {
app.post(
url = "https://path.gomuni.me/app/vapi.php",
data = mapOf("fid" to it.first, "func" to "hls")
).text.let { link ->
M3u8Helper.generateM3u8(
this.name,
link,
"$mainUrl/",
headers = mapOf("Origin" to mainUrl)
).forEach(callback)
}
}
it.second.contains("mp4") -> {
app.post(
url = "https://path.gomuni.me/app/vapi.php",
data = mapOf("data" to it.first, "func" to "blogs")
).parsed<List<MobiSource>>().map {
callback.invoke(
ExtractorLink(
source = name,
name = "Mobi SD",
url = it.file,
referer = "$mainUrl/",
quality = Qualities.P360.value
)
)
}
}
else -> null
}
}
}
return true
}
private data class Response(
@JsonProperty("status") val status: Boolean,
@JsonProperty("html") val html: String
)
data class MobiSource(
@JsonProperty("file") val file: String,
@JsonProperty("label") val label: String,
@JsonProperty("type") val type: String
)
private data class EpisodeElement(
@JsonProperty("data-index") val dataIndex: Long?,
@JsonProperty("ep-num") val epNum: String?,
@JsonProperty("ep-title") val epTitle: String?,
@JsonProperty("ep-link") val epLink: String,
@JsonProperty("ep-date") val epDate: String?
)
}

View File

@ -0,0 +1,14 @@
package com.lagradost
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
import com.lagradost.cloudstream3.plugins.Plugin
import android.content.Context
@CloudstreamPlugin
class GomunimeProviderPlugin: Plugin() {
override fun load(context: Context) {
// All providers should be added in this manner. Please don't edit the providers list directly.
registerMainAPI(GomunimeProvider())
}
}

View File

@ -0,0 +1,22 @@
// use an integer for version numbers
version = 1
cloudstream {
// All of these properties are optional, you can safely remove them
// description = "Lorem Ipsum"
// authors = listOf("Cloudburst")
/**
* Status int as the following:
* 0: Down
* 1: Ok
* 2: Slow
* 3: Beta only
* */
status = 1 // will be 3 if unspecified
// Set to true to get an 18+ symbol next to the plugin
adult = false // will be false if unspecified
}

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.lagradost"/>

View File

@ -0,0 +1,319 @@
package com.lagradost
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.M3u8Helper.Companion.generateM3u8
import com.lagradost.cloudstream3.utils.getQualityFromName
import com.lagradost.cloudstream3.utils.loadExtractor
import java.util.*
class JKAnimeProvider : MainAPI() {
companion object {
fun getType(t: String): TvType {
return if (t.contains("OVA") || t.contains("Especial")) TvType.OVA
else if (t.contains("Pelicula")) TvType.AnimeMovie
else TvType.Anime
}
}
override var mainUrl = "https://jkanime.net"
override var name = "JKAnime"
override var lang = "es"
override val hasMainPage = true
override val hasChromecastSupport = true
override val hasDownloadSupport = true
override val supportedTypes = setOf(
TvType.AnimeMovie,
TvType.OVA,
TvType.Anime,
)
override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse {
val urls = listOf(
Pair(
"$mainUrl/directorio/?filtro=fecha&tipo=TV&estado=1&fecha=none&temporada=none&orden=desc",
"En emisión"
),
Pair(
"$mainUrl/directorio/?filtro=fecha&tipo=none&estado=none&fecha=none&temporada=none&orden=none",
"Animes"
),
Pair(
"$mainUrl/directorio/?filtro=fecha&tipo=Movie&estado=none&fecha=none&temporada=none&orden=none",
"Películas"
),
)
val items = ArrayList<HomePageList>()
items.add(
HomePageList(
"Últimos episodios",
app.get(mainUrl).document.select(".listadoanime-home a.bloqq").map {
val title = it.selectFirst("h5")?.text()
val dubstat = if (title!!.contains("Latino") || title.contains("Castellano"))
DubStatus.Dubbed else DubStatus.Subbed
val poster =
it.selectFirst(".anime__sidebar__comment__item__pic img")?.attr("src") ?: ""
val epRegex = Regex("/(\\d+)/|/especial/|/ova/")
val url = it.attr("href").replace(epRegex, "")
val epNum =
it.selectFirst("h6")?.text()?.replace("Episodio ", "")?.toIntOrNull()
newAnimeSearchResponse(title, url) {
this.posterUrl = poster
addDubStatus(dubstat, epNum)
}
})
)
urls.apmap { (url, name) ->
val soup = app.get(url).document
val home = soup.select(".g-0").map {
val title = it.selectFirst("h5 a")?.text()
val poster = it.selectFirst("img")?.attr("src") ?: ""
AnimeSearchResponse(
title!!,
fixUrl(it.selectFirst("a")?.attr("href") ?: ""),
this.name,
TvType.Anime,
fixUrl(poster),
null,
if (title.contains("Latino") || title.contains("Castellano")) EnumSet.of(
DubStatus.Dubbed
) else EnumSet.of(DubStatus.Subbed),
)
}
items.add(HomePageList(name, home))
}
if (items.size <= 0) throw ErrorLoadingException()
return HomePageResponse(items)
}
data class MainSearch(
@JsonProperty("animes") val animes: List<Animes>,
@JsonProperty("anime_types") val animeTypes: AnimeTypes
)
data class Animes(
@JsonProperty("id") val id: String,
@JsonProperty("slug") val slug: String,
@JsonProperty("title") val title: String,
@JsonProperty("image") val image: String,
@JsonProperty("synopsis") val synopsis: String,
@JsonProperty("type") val type: String,
@JsonProperty("status") val status: String,
@JsonProperty("thumbnail") val thumbnail: String
)
data class AnimeTypes(
@JsonProperty("TV") val TV: String,
@JsonProperty("OVA") val OVA: String,
@JsonProperty("Movie") val Movie: String,
@JsonProperty("Special") val Special: String,
@JsonProperty("ONA") val ONA: String,
@JsonProperty("Music") val Music: String
)
override suspend fun search(query: String): List<SearchResponse> {
val main = app.get("$mainUrl/ajax/ajax_search/?q=$query").text
val json = parseJson<MainSearch>(main)
return json.animes.map {
val title = it.title
val href = "$mainUrl/${it.slug}"
val image = "https://cdn.jkanime.net/assets/images/animes/image/${it.slug}.jpg"
AnimeSearchResponse(
title,
href,
this.name,
TvType.Anime,
image,
null,
if (title.contains("Latino") || title.contains("Castellano")) EnumSet.of(
DubStatus.Dubbed
) else EnumSet.of(DubStatus.Subbed),
)
}
}
override suspend fun load(url: String): LoadResponse {
val doc = app.get(url, timeout = 120).document
val poster = doc.selectFirst(".set-bg")?.attr("data-setbg")
val title = doc.selectFirst(".anime__details__title > h3")?.text()
val type = doc.selectFirst(".anime__details__text")?.text()
val description = doc.selectFirst(".anime__details__text > p")?.text()
val genres = doc.select("div.col-lg-6:nth-child(1) > ul:nth-child(1) > li:nth-child(2) > a")
.map { it.text() }
val status = when (doc.selectFirst("span.enemision")?.text()) {
"En emisión" -> ShowStatus.Ongoing
"Concluido" -> ShowStatus.Completed
else -> null
}
val animeID = doc.selectFirst("div.ml-2")?.attr("data-anime")?.toInt()
val animeeps = "$mainUrl/ajax/last_episode/$animeID/"
val jsoneps = app.get(animeeps).text
val lastepnum =
jsoneps.substringAfter("{\"number\":\"").substringBefore("\",\"title\"").toInt()
val episodes = (1..lastepnum).map {
val link = "${url.removeSuffix("/")}/$it"
Episode(link)
}
return newAnimeLoadResponse(title!!, url, getType(type!!)) {
posterUrl = poster
addEpisodes(DubStatus.Subbed, episodes)
showStatus = status
plot = description
tags = genres
}
}
data class Nozomi(
@JsonProperty("file") val file: String?
)
private fun streamClean(
name: String,
url: String,
referer: String,
quality: String?,
callback: (ExtractorLink) -> Unit,
m3u8: Boolean
): Boolean {
callback(
ExtractorLink(
name,
name,
url,
referer,
getQualityFromName(quality),
m3u8
)
)
return true
}
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
app.get(data).document.select("script").apmap { script ->
if (script.data().contains("var video = []")) {
val videos = script.data().replace("\\/", "/")
fetchUrls(videos).map {
it.replace("$mainUrl/jkfembed.php?u=", "https://embedsito.com/v/")
.replace("$mainUrl/jkokru.php?u=", "http://ok.ru/videoembed/")
.replace("$mainUrl/jkvmixdrop.php?u=", "https://mixdrop.co/e/")
.replace("$mainUrl/jk.php?u=", "$mainUrl/")
}.apmap { link ->
loadExtractor(link, data, subtitleCallback, callback)
if (link.contains("um2.php")) {
val doc = app.get(link, referer = data).document
val gsplaykey = doc.select("form input[value]").attr("value")
app.post(
"$mainUrl/gsplay/redirect_post.php",
headers = mapOf(
"Host" to "jkanime.net",
"User-Agent" to USER_AGENT,
"Accept" to "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
"Accept-Language" to "en-US,en;q=0.5",
"Referer" to link,
"Content-Type" to "application/x-www-form-urlencoded",
"Origin" to "https://jkanime.net",
"DNT" to "1",
"Connection" to "keep-alive",
"Upgrade-Insecure-Requests" to "1",
"Sec-Fetch-Dest" to "iframe",
"Sec-Fetch-Mode" to "navigate",
"Sec-Fetch-Site" to "same-origin",
"TE" to "trailers",
"Pragma" to "no-cache",
"Cache-Control" to "no-cache",
),
data = mapOf(Pair("data", gsplaykey)),
allowRedirects = false
).okhttpResponse.headers.values("location").apmap { loc ->
val postkey = loc.replace("/gsplay/player.html#", "")
val nozomitext = app.post(
"$mainUrl/gsplay/api.php",
headers = mapOf(
"Host" to "jkanime.net",
"User-Agent" to USER_AGENT,
"Accept" to "application/json, text/javascript, */*; q=0.01",
"Accept-Language" to "en-US,en;q=0.5",
"Content-Type" to "application/x-www-form-urlencoded; charset=UTF-8",
"X-Requested-With" to "XMLHttpRequest",
"Origin" to "https://jkanime.net",
"DNT" to "1",
"Connection" to "keep-alive",
"Sec-Fetch-Dest" to "empty",
"Sec-Fetch-Mode" to "cors",
"Sec-Fetch-Site" to "same-origin",
),
data = mapOf(Pair("v", postkey)),
allowRedirects = false
).text
val json = parseJson<Nozomi>(nozomitext)
val nozomiurl = listOf(json.file)
if (nozomiurl.isEmpty()) null else
nozomiurl.forEach { url ->
val nozominame = "Nozomi"
streamClean(
nozominame,
url!!,
"",
null,
callback,
url.contains(".m3u8")
)
}
}
}
if (link.contains("um.php")) {
val desutext = app.get(link, referer = data).text
val desuRegex = Regex("((https:|http:)//.*\\.m3u8)")
val file = desuRegex.find(desutext)?.value
val namedesu = "Desu"
generateM3u8(
namedesu,
file!!,
mainUrl,
).forEach { desurl ->
streamClean(
namedesu,
desurl.url,
mainUrl,
desurl.quality.toString(),
callback,
true
)
}
}
if (link.contains("jkmedia")) {
app.get(
link,
referer = data,
allowRedirects = false
).okhttpResponse.headers.values("location").apmap { xtremeurl ->
val namex = "Xtreme S"
streamClean(
namex,
xtremeurl,
"",
null,
callback,
xtremeurl.contains(".m3u8")
)
}
}
}
}
}
return true
}
}

View File

@ -0,0 +1,14 @@
package com.lagradost
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
import com.lagradost.cloudstream3.plugins.Plugin
import android.content.Context
@CloudstreamPlugin
class JKAnimeProviderPlugin: Plugin() {
override fun load(context: Context) {
// All providers should be added in this manner. Please don't edit the providers list directly.
registerMainAPI(JKAnimeProvider())
}
}

View File

@ -0,0 +1,22 @@
// use an integer for version numbers
version = 1
cloudstream {
// All of these properties are optional, you can safely remove them
// description = "Lorem Ipsum"
// authors = listOf("Cloudburst")
/**
* Status int as the following:
* 0: Down
* 1: Ok
* 2: Slow
* 3: Beta only
* */
status = 1 // will be 3 if unspecified
// Set to true to get an 18+ symbol next to the plugin
adult = false // will be false if unspecified
}

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.lagradost"/>

View File

@ -0,0 +1,174 @@
package com.lagradost
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.getQualityFromName
import org.jsoup.Jsoup
import java.util.*
class KawaiifuProvider : MainAPI() {
override var mainUrl = "https://kawaiifu.com"
override var name = "Kawaiifu"
override val hasQuickSearch = false
override val hasMainPage = true
override val supportedTypes = setOf(TvType.Anime, TvType.AnimeMovie)
override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse {
val items = ArrayList<HomePageList>()
val resp = app.get(mainUrl).text
val soup = Jsoup.parse(resp)
items.add(HomePageList("Latest Updates", soup.select(".today-update .item").mapNotNull {
val title = it.selectFirst("img")?.attr("alt")
AnimeSearchResponse(
title ?: return@mapNotNull null,
it.selectFirst("a")?.attr("href") ?: return@mapNotNull null,
this.name,
TvType.Anime,
it.selectFirst("img")?.attr("src"),
it.selectFirst("h4 > a")?.attr("href")?.split("-")?.last()?.toIntOrNull(),
if (title.contains("(DUB)")) EnumSet.of(DubStatus.Dubbed) else EnumSet.of(
DubStatus.Subbed
),
)
}))
for (section in soup.select(".section")) {
try {
val title = section.selectFirst(".title")!!.text()
val anime = section.select(".list-film > .item").mapNotNull { ani ->
val animTitle = ani.selectFirst("img")?.attr("alt")
AnimeSearchResponse(
animTitle ?: return@mapNotNull null,
ani.selectFirst("a")?.attr("href") ?: return@mapNotNull null,
this.name,
TvType.Anime,
ani.selectFirst("img")?.attr("src"),
ani.selectFirst(".vl-chil-date")?.text()?.toIntOrNull(),
if (animTitle.contains("(DUB)")) EnumSet.of(DubStatus.Dubbed) else EnumSet.of(
DubStatus.Subbed
),
)
}
items.add(HomePageList(title, anime))
} catch (e: Exception) {
e.printStackTrace()
}
}
if (items.size <= 0) throw ErrorLoadingException()
return HomePageResponse(items)
}
override suspend fun search(query: String): ArrayList<SearchResponse> {
val link = "$mainUrl/search-movie?keyword=${query}"
val html = app.get(link).text
val soup = Jsoup.parse(html)
return ArrayList(soup.select(".item").mapNotNull {
val year = it.selectFirst("h4 > a")?.attr("href")?.split("-")?.last()?.toIntOrNull()
val title = it.selectFirst("img")?.attr("alt") ?: return@mapNotNull null
val poster = it.selectFirst("img")?.attr("src")
val uri = it.selectFirst("a")?.attr("href") ?: return@mapNotNull null
AnimeSearchResponse(
title,
uri,
this.name,
TvType.Anime,
poster,
year,
if (title.contains("(DUB)")) EnumSet.of(DubStatus.Dubbed) else EnumSet.of(DubStatus.Subbed),
)
})
}
override suspend fun load(url: String): LoadResponse {
val html = app.get(url).text
val soup = Jsoup.parse(html)
val title = soup.selectFirst(".title")!!.text()
val tags = soup.select(".table a[href*=\"/tag/\"]").map { tag -> tag.text() }
val description = soup.select(".sub-desc p")
.filter { it -> it.select("strong").isEmpty() && it.select("iframe").isEmpty() }
.joinToString("\n") { it.text() }
val year = url.split("/").filter { it.contains("-") }[0].split("-")[1].toIntOrNull()
val episodesLink = soup.selectFirst("a[href*=\".html-episode\"]")?.attr("href")
?: throw ErrorLoadingException("Error getting episode list")
val episodes = Jsoup.parse(
app.get(episodesLink).text
).selectFirst(".list-ep")?.select("li")?.map {
Episode(
it.selectFirst("a")!!.attr("href"),
if (it.text().trim().toIntOrNull() != null) "Episode ${
it.text().trim()
}" else it.text().trim()
)
}
val poster = soup.selectFirst("a.thumb > img")?.attr("src")
return newAnimeLoadResponse(title, url, TvType.Anime) {
this.year = year
posterUrl = poster
addEpisodes(DubStatus.Subbed, episodes)
plot = description
this.tags = tags
}
}
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
val htmlSource = app.get(data).text
val soupa = Jsoup.parse(htmlSource)
val episodeNum =
if (data.contains("ep=")) data.split("ep=")[1].split("&")[0].toIntOrNull() else null
val servers = soupa.select(".list-server").map {
val serverName = it.selectFirst(".server-name")!!.text()
val episodes = it.select(".list-ep > li > a")
.map { episode -> Pair(episode.attr("href"), episode.text()) }
val episode = if (episodeNum == null) episodes[0] else episodes.mapNotNull { ep ->
if ((if (ep.first.contains("ep=")) ep.first.split("ep=")[1].split("&")[0].toIntOrNull() else null) == episodeNum) {
ep
} else null
}[0]
Pair(serverName, episode)
}.map {
if (it.second.first == data) {
val sources = soupa.select("video > source")
.map { source -> Pair(source.attr("src"), source.attr("data-quality")) }
Triple(it.first, sources, it.second.second)
} else {
val html = app.get(it.second.first).text
val soup = Jsoup.parse(html)
val sources = soup.select("video > source")
.map { source -> Pair(source.attr("src"), source.attr("data-quality")) }
Triple(it.first, sources, it.second.second)
}
}
servers.forEach {
it.second.forEach { source ->
callback(
ExtractorLink(
"Kawaiifu",
it.first,
source.first,
"",
getQualityFromName(source.second),
source.first.contains(".m3u")
)
)
}
}
return true
}
}

View File

@ -0,0 +1,14 @@
package com.lagradost
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
import com.lagradost.cloudstream3.plugins.Plugin
import android.content.Context
@CloudstreamPlugin
class KawaiifuProviderPlugin: Plugin() {
override fun load(context: Context) {
// All providers should be added in this manner. Please don't edit the providers list directly.
registerMainAPI(KawaiifuProvider())
}
}

View File

@ -0,0 +1,22 @@
// use an integer for version numbers
version = 1
cloudstream {
// All of these properties are optional, you can safely remove them
// description = "Lorem Ipsum"
// authors = listOf("Cloudburst")
/**
* Status int as the following:
* 0: Down
* 1: Ok
* 2: Slow
* 3: Beta only
* */
status = 1 // will be 3 if unspecified
// Set to true to get an 18+ symbol next to the plugin
adult = false // will be false if unspecified
}

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.lagradost"/>

View File

@ -0,0 +1,152 @@
package com.lagradost
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.loadExtractor
class KimCartoonProvider : MainAPI() {
override var mainUrl = "https://kimcartoon.li"
override var name = "Kim Cartoon"
override val hasQuickSearch = true
override val hasMainPage = true
override val supportedTypes = setOf(TvType.Cartoon)
private fun fixUrl(url: String): String {
return if (url.startsWith("/")) mainUrl + url else url
}
override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse {
val doc = app.get(mainUrl).document.select("#container")
val response = mutableListOf(
HomePageList(
"Latest Update",
doc.select("div.bigBarContainer div.items > div > a").map {
AnimeSearchResponse(
it.select(".item-title").let { div ->
//Because it doesn't contain Title separately
div.text().replace(div.select("span").text(), "")
},
mainUrl + it.attr("href"),
mainUrl,
TvType.Cartoon,
fixUrl(it.select("img").let { img ->
img.attr("src").let { src ->
src.ifEmpty { img.attr("srctemp") }
}
})
)
}
)
)
val list = mapOf(
"Top Day" to "tab-top-day",
"Top Week" to "tab-top-week",
"Top Month" to "tab-top-month",
"New Cartoons" to "tab-newest-series"
)
response.addAll(list.map { item ->
HomePageList(
item.key,
doc.select("#${item.value} > div").map {
AnimeSearchResponse(
it.select("span.title").text(),
mainUrl + it.select("a")[0].attr("href"),
mainUrl,
TvType.Cartoon,
fixUrl(it.select("a > img").attr("src"))
)
}
)
})
return HomePageResponse(response)
}
override suspend fun search(query: String): List<SearchResponse> {
return app.post(
"$mainUrl/Search/Cartoon",
data = mapOf("keyword" to query)
).document
.select("#leftside > div.bigBarContainer div.list-cartoon > div.item > a")
.map {
AnimeSearchResponse(
it.select("span").text(),
mainUrl + it.attr("href"),
mainUrl,
TvType.Cartoon,
fixUrl(it.select("img").attr("src"))
)
}
}
override suspend fun quickSearch(query: String): List<SearchResponse> {
return app.post(
"$mainUrl/Ajax/SearchSuggest",
data = mapOf("keyword" to query)
).document.select("a").map {
AnimeSearchResponse(
it.text(),
it.attr("href"),
mainUrl,
TvType.Cartoon,
)
}
}
private fun getStatus(from: String?): ShowStatus? {
return when {
from?.contains("Completed") == true -> ShowStatus.Completed
from?.contains("Ongoing") == true -> ShowStatus.Ongoing
else -> null
}
}
override suspend fun load(url: String): LoadResponse {
val doc = app.get(url).document.select("#leftside")
val info = doc.select("div.barContent")
val name = info.select("a.bigChar").text()
val eps = doc.select("table.listing > tbody > tr a").reversed().map {
Episode(
fixUrl(it.attr("href")),
it.text().replace(name, "").trim()
)
}
val infoText = info.text()
fun getData(after: String, before: String): String? {
return if (infoText.contains(after))
infoText
.substringAfter("$after:")
.substringBefore(before)
.trim()
else null
}
return newTvSeriesLoadResponse(name, url, TvType.Cartoon, eps) {
posterUrl = fixUrl(info.select("div > img").attr("src"))
showStatus = getStatus(getData("Status", "Views"))
plot = getData("Summary", "Tags:")
tags = getData("Genres", "Date aired")?.split(",")
}
}
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
val servers =
app.get(data).document.select("#selectServer > option").map { fixUrl(it.attr("value")) }
servers.apmap {
app.get(it).document.select("#my_video_1").attr("src").let { iframe ->
if (iframe.isNotEmpty()) {
loadExtractor(iframe, "$mainUrl/", subtitleCallback, callback)
}
//There are other servers, but they require some work to do
}
}
return true
}
}

View File

@ -0,0 +1,14 @@
package com.lagradost
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
import com.lagradost.cloudstream3.plugins.Plugin
import android.content.Context
@CloudstreamPlugin
class KimCartoonProviderPlugin: Plugin() {
override fun load(context: Context) {
// All providers should be added in this manner. Please don't edit the providers list directly.
registerMainAPI(KimCartoonProvider())
}
}

View File

@ -0,0 +1,22 @@
// use an integer for version numbers
version = 1
cloudstream {
// All of these properties are optional, you can safely remove them
// description = "Lorem Ipsum"
// authors = listOf("Cloudburst")
/**
* Status int as the following:
* 0: Down
* 1: Ok
* 2: Slow
* 3: Beta only
* */
status = 1 // will be 3 if unspecified
// Set to true to get an 18+ symbol next to the plugin
adult = false // will be false if unspecified
}

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.lagradost"/>

View File

@ -0,0 +1,176 @@
package com.lagradost
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall
import com.lagradost.cloudstream3.utils.ExtractorLink
import org.jsoup.Jsoup
import org.jsoup.nodes.Element
class KuramanimeProvider : MainAPI() {
override var mainUrl = "https://kuramanime.com"
override var name = "Kuramanime"
override val hasQuickSearch = false
override val hasMainPage = true
override var lang = "id"
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") || t.contains("Special")) TvType.OVA
else if (t.contains("Movie")) TvType.AnimeMovie
else TvType.Anime
}
fun getStatus(t: String): ShowStatus {
return when (t) {
"Selesai Tayang" -> ShowStatus.Completed
"Sedang Tayang" -> ShowStatus.Ongoing
else -> ShowStatus.Completed
}
}
}
override val mainPage = mainPageOf(
"$mainUrl/anime/ongoing?order_by=updated&page=" to "Sedang Tayang",
"$mainUrl/anime/finished?order_by=updated&page=" to "Selesai Tayang",
"$mainUrl/properties/season/summer-2022?order_by=most_viewed&page=" to "Dilihat Terbanyak Musim Ini",
"$mainUrl/anime/movie?order_by=updated&page=" to "Film Layar Lebar",
)
override suspend fun getMainPage(
page: Int,
request: MainPageRequest
): HomePageResponse {
val document = app.get(request.data + page).document
val home = document.select("div.col-lg-4.col-md-6.col-sm-6").mapNotNull {
it.toSearchResult()
}
return newHomePageResponse(request.name, home)
}
private fun getProperAnimeLink(uri: String): String {
return if (uri.contains("/episode")) {
Regex("(.*)/episode/.+").find(uri)?.groupValues?.get(1).toString() + "/"
} else {
uri
}
}
private fun Element.toSearchResult(): AnimeSearchResponse? {
val href = getProperAnimeLink(fixUrl(this.selectFirst("a")!!.attr("href")))
val title = this.selectFirst("h5 a")?.text() ?: return null
val posterUrl = fixUrl(this.select("div.product__item__pic.set-bg").attr("data-setbg"))
val episode = Regex("([0-9*])\\s?/").find(
this.select("div.ep span").text()
)?.groupValues?.getOrNull(1)?.toIntOrNull()
return newAnimeSearchResponse(title, href, TvType.Anime) {
this.posterUrl = posterUrl
addSub(episode)
}
}
override suspend fun search(query: String): List<SearchResponse> {
val link = "$mainUrl/anime?search=$query&order_by=oldest"
val document = app.get(link).document
return document.select(".product__item").mapNotNull {
val title = it.selectFirst("div.product__item__text > h5")!!.text().trim()
val poster = it.selectFirst("a > div")!!.attr("data-setbg")
val tvType =
getType(it.selectFirst(".product__item__text > ul > li")!!.text().toString())
val href = fixUrl(it.selectFirst("a")!!.attr("href"))
newAnimeSearchResponse(title, href, tvType) {
this.posterUrl = poster
addDubStatus(dubExist = false, subExist = true)
}
}
}
override suspend fun load(url: String): LoadResponse {
val document = app.get(url).document
val title = document.selectFirst(".anime__details__title > h3")!!.text().trim()
val poster = document.selectFirst(".anime__details__pic")?.attr("data-setbg")
val tags =
document.select("div.anime__details__widget > div > div:nth-child(2) > ul > li:nth-child(1)")
.text().trim().replace("Genre: ", "").split(", ")
val year = Regex("[^0-9]").replace(
document.select("div.anime__details__widget > div > div:nth-child(1) > ul > li:nth-child(5)")
.text().trim().replace("Musim: ", ""), ""
).toIntOrNull()
val status = getStatus(
document.select("div.anime__details__widget > div > div:nth-child(1) > ul > li:nth-child(3)")
.text().trim().replace("Status: ", "")
)
val description = document.select(".anime__details__text > p").text().trim()
val episodes =
Jsoup.parse(document.select("#episodeLists").attr("data-content")).select("a").map {
val name = it.text().trim()
val link = it.attr("href")
Episode(link, name)
}
val recommendations = document.select("div#randomList > a").mapNotNull {
val epHref = it.attr("href")
val epTitle = it.select("h5.sidebar-title-h5.px-2.py-2").text()
val epPoster = it.select(".product__sidebar__view__item.set-bg").attr("data-setbg")
newAnimeSearchResponse(epTitle, epHref, TvType.Anime) {
this.posterUrl = epPoster
addDubStatus(dubExist = false, subExist = true)
}
}
return newAnimeLoadResponse(title, url, TvType.Anime) {
engName = title
posterUrl = poster
this.year = year
addEpisodes(DubStatus.Subbed, episodes)
showStatus = status
plot = description
this.tags = tags
this.recommendations = recommendations
}
}
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
val servers = app.get(data).document
servers.select("video#player > source").map {
suspendSafeApiCall {
val url = it.attr("src")
val quality = it.attr("size").toInt()
callback.invoke(
ExtractorLink(
name,
name,
url,
referer = "$mainUrl/",
quality = quality
)
)
}
}
return true
}
}

View File

@ -0,0 +1,14 @@
package com.lagradost
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
import com.lagradost.cloudstream3.plugins.Plugin
import android.content.Context
@CloudstreamPlugin
class KuramanimeProviderPlugin: Plugin() {
override fun load(context: Context) {
// All providers should be added in this manner. Please don't edit the providers list directly.
registerMainAPI(KuramanimeProvider())
}
}

View File

@ -0,0 +1,22 @@
// use an integer for version numbers
version = 1
cloudstream {
// All of these properties are optional, you can safely remove them
// description = "Lorem Ipsum"
// authors = listOf("Cloudburst")
/**
* Status int as the following:
* 0: Down
* 1: Ok
* 2: Slow
* 3: Beta only
* */
status = 1 // will be 3 if unspecified
// Set to true to get an 18+ symbol next to the plugin
adult = false // will be false if unspecified
}

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.lagradost"/>

View File

@ -0,0 +1,197 @@
package com.lagradost
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
import com.lagradost.cloudstream3.mvvm.safeApiCall
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.getQualityFromName
import com.lagradost.cloudstream3.utils.loadExtractor
import org.jsoup.Jsoup
import org.jsoup.nodes.Element
class KuronimeProvider : MainAPI() {
override var mainUrl = "https://45.12.2.2"
override var name = "Kuronime"
override val hasQuickSearch = false
override val hasMainPage = true
override var lang = "id"
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") || t.contains("Special")) TvType.OVA
else if (t.contains("Movie")) TvType.AnimeMovie
else TvType.Anime
}
fun getStatus(t: String): ShowStatus {
return when (t) {
"Completed" -> ShowStatus.Completed
"Ongoing" -> ShowStatus.Ongoing
else -> ShowStatus.Completed
}
}
}
override val mainPage = mainPageOf(
"$mainUrl/page/" to "New Episodes",
"$mainUrl/popular-anime/page/" to "Popular Anime",
"$mainUrl/movies/page/" to "Movies",
"$mainUrl/genres/donghua/page/" to "Donghua",
"$mainUrl/live-action/page/" to "Live Action",
)
override suspend fun getMainPage(
page: Int,
request: MainPageRequest
): HomePageResponse {
val document = app.get(request.data + page).document
val home = document.select("article").map {
it.toSearchResult()
}
return newHomePageResponse(request.name, home)
}
private fun getProperAnimeLink(uri: String): String {
return if (uri.contains("/anime/")) {
uri
} else {
var title = uri.substringAfter("$mainUrl/")
title = when {
(title.contains("-episode")) && !(title.contains("-movie")) -> Regex("nonton-(.+)-episode").find(
title
)?.groupValues?.get(1).toString()
(title.contains("-movie")) -> Regex("nonton-(.+)-movie").find(title)?.groupValues?.get(
1
).toString()
else -> title
}
"$mainUrl/anime/$title"
}
}
private fun Element.toSearchResult(): AnimeSearchResponse {
val href = getProperAnimeLink(fixUrlNull(this.selectFirst("a")?.attr("href")).toString())
val title = this.select(".bsuxtt, .tt > h4").text().trim()
val posterUrl = fixUrlNull(
this.selectFirst("div.view,div.bt")?.nextElementSibling()?.select("img")
?.attr("data-src")
)
val epNum = this.select(".ep").text().replace(Regex("[^0-9]"), "").trim().toIntOrNull()
val tvType = getType(this.selectFirst(".bt > span")?.text().toString())
return newAnimeSearchResponse(title, href, tvType) {
this.posterUrl = posterUrl
addSub(epNum)
}
}
override suspend fun search(query: String): List<SearchResponse> {
val link = "$mainUrl/?s=$query"
val document = app.get(link).document
return document.select("article.bs").map {
it.toSearchResult()
}
}
override suspend fun load(url: String): LoadResponse {
val document = app.get(url).document
val title = document.selectFirst(".entry-title")?.text().toString().trim()
val poster = document.selectFirst("div.l[itemprop=image] > img")?.attr("data-src")
val tags = document.select(".infodetail > ul > li:nth-child(2) > a").map { it.text() }
val type = getType(
document.selectFirst(".infodetail > ul > li:nth-child(7)")?.ownText()?.trim().toString()
)
val trailer = document.selectFirst("div.tply iframe")?.attr("data-lazy-src")
val year = Regex("\\d, ([0-9]*)").find(
document.select(".infodetail > ul > li:nth-child(5)").text()
)?.groupValues?.get(1)?.toIntOrNull()
val status = getStatus(
document.selectFirst(".infodetail > ul > li:nth-child(3)")!!.ownText()
.replace(Regex("\\W"), "")
)
val description = document.select("span.const > p").text()
val episodes = document.select("div.bixbox.bxcl > ul > li").map {
val name = it.selectFirst("a")?.text()?.trim()
val episode =
it.selectFirst("a")?.text()?.trim()?.replace("Episode", "")?.trim()?.toIntOrNull()
val link = it.selectFirst("a")!!.attr("href")
Episode(link, name = name, episode = episode)
}.reversed()
return newAnimeLoadResponse(title, url, type) {
engName = title
posterUrl = poster
this.year = year
addEpisodes(DubStatus.Subbed, episodes)
showStatus = status
plot = description
addTrailer(trailer)
this.tags = tags
}
}
private suspend fun invokeKuroSource(
url: String,
sourceCallback: (ExtractorLink) -> Unit
) {
val doc = app.get(url, referer = "${mainUrl}/").document
doc.select("script").map { script ->
if (script.data().contains("function jalankan_jwp() {")) {
val data = script.data()
val doma = data.substringAfter("var doma = \"").substringBefore("\";")
val token = data.substringAfter("var token = \"").substringBefore("\";")
val pat = data.substringAfter("var pat = \"").substringBefore("\";")
val link = "$doma$token$pat/index.m3u8"
val quality =
Regex("\\d{3,4}p").find(doc.select("title").text())?.groupValues?.get(0)
sourceCallback.invoke(
ExtractorLink(
this.name,
this.name,
link,
referer = "https://animeku.org/",
quality = getQualityFromName(quality),
headers = mapOf("Origin" to "https://animeku.org"),
isM3u8 = true
)
)
}
}
}
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
val document = app.get(data).document
val sources = document.select(".mobius > .mirror > option").mapNotNull {
fixUrl(Jsoup.parse(base64Decode(it.attr("value"))).select("iframe").attr("data-src"))
}
sources.apmap {
safeApiCall {
when {
it.startsWith("https://animeku.org") -> invokeKuroSource(it, callback)
else -> loadExtractor(it, mainUrl, subtitleCallback, callback)
}
}
}
return true
}
}

View File

@ -0,0 +1,14 @@
package com.lagradost
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
import com.lagradost.cloudstream3.plugins.Plugin
import android.content.Context
@CloudstreamPlugin
class KuronimeProviderPlugin: Plugin() {
override fun load(context: Context) {
// All providers should be added in this manner. Please don't edit the providers list directly.
registerMainAPI(KuronimeProvider())
}
}

View File

@ -0,0 +1,22 @@
// use an integer for version numbers
version = 1
cloudstream {
// All of these properties are optional, you can safely remove them
// description = "Lorem Ipsum"
// authors = listOf("Cloudburst")
/**
* Status int as the following:
* 0: Down
* 1: Ok
* 2: Slow
* 3: Beta only
* */
status = 1 // will be 3 if unspecified
// Set to true to get an 18+ symbol next to the plugin
adult = false // will be false if unspecified
}

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.lagradost"/>

View File

@ -0,0 +1,155 @@
package com.lagradost
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.loadExtractor
import java.util.*
class MonoschinosProvider : MainAPI() {
companion object {
fun getType(t: String): TvType {
return if (t.contains("OVA") || t.contains("Especial")) TvType.OVA
else if (t.contains("Pelicula")) TvType.AnimeMovie
else TvType.Anime
}
fun getDubStatus(title: String): DubStatus {
return if (title.contains("Latino") || title.contains("Castellano"))
DubStatus.Dubbed
else DubStatus.Subbed
}
}
override var mainUrl = "https://monoschinos2.com"
override var name = "Monoschinos"
override var lang = "es"
override val hasMainPage = true
override val hasChromecastSupport = true
override val hasDownloadSupport = true
override val supportedTypes = setOf(
TvType.AnimeMovie,
TvType.OVA,
TvType.Anime,
)
override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse {
val urls = listOf(
Pair("$mainUrl/emision", "En emisión"),
Pair(
"$mainUrl/animes?categoria=pelicula&genero=false&fecha=false&letra=false",
"Peliculas"
),
Pair("$mainUrl/animes", "Animes"),
)
val items = ArrayList<HomePageList>()
items.add(
HomePageList(
"Capítulos actualizados",
app.get(mainUrl, timeout = 120).document.select(".col-6").map {
val title = it.selectFirst("p.animetitles")?.text() ?: it.selectFirst(".animetitles")?.text() ?: ""
val poster = it.selectFirst(".animeimghv")!!.attr("data-src")
val epRegex = Regex("episodio-(\\d+)")
val url = it.selectFirst("a")?.attr("href")!!.replace("ver/", "anime/")
.replace(epRegex, "sub-espanol")
val epNum = (it.selectFirst(".positioning h5")?.text() ?: it.selectFirst("div.positioning p")?.text())?.toIntOrNull()
newAnimeSearchResponse(title, url) {
this.posterUrl = fixUrl(poster)
addDubStatus(getDubStatus(title), epNum)
}
})
)
for (i in urls) {
try {
val home = app.get(i.first, timeout = 120).document.select(".col-6").map {
val title = it.selectFirst(".seristitles")!!.text()
val poster = it.selectFirst("img.animemainimg")!!.attr("src")
newAnimeSearchResponse(title, fixUrl(it.selectFirst("a")!!.attr("href"))) {
this.posterUrl = fixUrl(poster)
addDubStatus(getDubStatus(title))
}
}
items.add(HomePageList(i.second, home))
} catch (e: Exception) {
e.printStackTrace()
}
}
if (items.size <= 0) throw ErrorLoadingException()
return HomePageResponse(items)
}
override suspend fun search(query: String): ArrayList<SearchResponse> {
val search =
app.get("$mainUrl/buscar?q=$query", timeout = 120).document.select(".col-6").map {
val title = it.selectFirst(".seristitles")!!.text()
val href = fixUrl(it.selectFirst("a")!!.attr("href"))
val image = it.selectFirst("img.animemainimg")!!.attr("src")
AnimeSearchResponse(
title,
href,
this.name,
TvType.Anime,
fixUrl(image),
null,
if (title.contains("Latino") || title.contains("Castellano")) EnumSet.of(
DubStatus.Dubbed
) else EnumSet.of(DubStatus.Subbed),
)
}
return ArrayList(search)
}
override suspend fun load(url: String): LoadResponse {
val doc = app.get(url, timeout = 120).document
val poster = doc.selectFirst(".chapterpic img")!!.attr("src")
val title = doc.selectFirst(".chapterdetails h1")!!.text()
val type = doc.selectFirst("div.chapterdetls2")!!.text()
val description = doc.selectFirst("p.textComplete")!!.text().replace("Ver menos", "")
val genres = doc.select(".breadcrumb-item a").map { it.text() }
val status = when (doc.selectFirst("button.btn1")?.text()) {
"Estreno" -> ShowStatus.Ongoing
"Finalizado" -> ShowStatus.Completed
else -> null
}
val episodes = doc.select("div.col-item").map {
val name = it.selectFirst("p.animetitles")!!.text()
val link = it.selectFirst("a")!!.attr("href")
val epThumb = it.selectFirst(".animeimghv")!!.attr("data-src")
Episode(link, name, posterUrl = epThumb)
}
return newAnimeLoadResponse(title, url, getType(type)) {
posterUrl = poster
addEpisodes(DubStatus.Subbed, episodes)
showStatus = status
plot = description
tags = genres
}
}
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
app.get(data).document.select("div.playother p").forEach {
val encodedurl = it.select("p").attr("data-player")
val urlDecoded = base64Decode(encodedurl)
val url = (urlDecoded).replace("https://monoschinos2.com/reproductor?url=", "")
if (url.startsWith("https://www.fembed.com")) {
val extractor = FEmbed()
extractor.getUrl(url).forEach { link ->
callback.invoke(link)
}
} else {
loadExtractor(url, mainUrl, subtitleCallback, callback)
}
}
return true
}
}

View File

@ -0,0 +1,14 @@
package com.lagradost
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
import com.lagradost.cloudstream3.plugins.Plugin
import android.content.Context
@CloudstreamPlugin
class MonoschinosProviderPlugin: Plugin() {
override fun load(context: Context) {
// All providers should be added in this manner. Please don't edit the providers list directly.
registerMainAPI(MonoschinosProvider())
}
}

View File

@ -0,0 +1,67 @@
package com.lagradost
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.AppUtils
import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.getQualityFromName
class FEmbed: XStreamCdn() {
override val name: String = "FEmbed"
override val mainUrl: String = "https://www.fembed.com"
}
open class XStreamCdn : ExtractorApi() {
override val name: String = "XStreamCdn"
override val mainUrl: String = "https://embedsito.com"
override val requiresReferer = false
open var domainUrl: String = "embedsito.com"
private data class ResponseData(
@JsonProperty("file") val file: String,
@JsonProperty("label") val label: String,
//val type: String // Mp4
)
private data class ResponseJson(
@JsonProperty("success") val success: Boolean,
@JsonProperty("data") val data: List<ResponseData>?
)
override fun getExtractorUrl(id: String): String {
return "$domainUrl/api/source/$id"
}
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink> {
val headers = mapOf(
"Referer" to url,
"User-Agent" to "Mozilla/5.0 (Windows NT 10.0; rv:78.0) Gecko/20100101 Firefox/78.0",
)
val id = url.trimEnd('/').split("/").last()
val newUrl = "https://${domainUrl}/api/source/${id}"
val extractedLinksList: MutableList<ExtractorLink> = mutableListOf()
with(app.post(newUrl, headers = headers)) {
if (this.code != 200) return listOf()
val text = this.text
if (text.isEmpty()) return listOf()
if (text == """{"success":false,"data":"Video not found or has been removed"}""") return listOf()
AppUtils.parseJson<ResponseJson?>(text)?.let {
if (it.success && it.data != null) {
it.data.forEach { data ->
extractedLinksList.add(
ExtractorLink(
name,
name = name,
data.file,
url,
getQualityFromName(data.label),
)
)
}
}
}
}
return extractedLinksList
}
}

View File

@ -0,0 +1,22 @@
// use an integer for version numbers
version = 1
cloudstream {
// All of these properties are optional, you can safely remove them
// description = "Lorem Ipsum"
// authors = listOf("Cloudburst")
/**
* Status int as the following:
* 0: Down
* 1: Ok
* 2: Slow
* 3: Beta only
* */
status = 1 // will be 3 if unspecified
// Set to true to get an 18+ symbol next to the plugin
adult = false // will be false if unspecified
}

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.lagradost"/>

View File

@ -0,0 +1,217 @@
package com.lagradost
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.M3u8Helper.Companion.generateM3u8
import com.lagradost.cloudstream3.utils.getAndUnpack
import com.lagradost.cloudstream3.utils.getQualityFromName
import com.lagradost.cloudstream3.utils.loadExtractor
import java.util.*
class MundoDonghuaProvider : MainAPI() {
override var mainUrl = "https://www.mundodonghua.com"
override var name = "MundoDonghua"
override var lang = "es"
override val hasMainPage = true
override val hasChromecastSupport = true
override val hasDownloadSupport = true
override val supportedTypes = setOf(
TvType.Anime,
)
override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse {
val urls = listOf(
Pair("$mainUrl/lista-donghuas", "Donghuas"),
)
val items = ArrayList<HomePageList>()
items.add(
HomePageList(
"Últimos episodios",
app.get(mainUrl, timeout = 120).document.select("div.row .col-xs-4").map {
val title = it.selectFirst("h5")?.text() ?: ""
val poster = it.selectFirst(".fit-1 img")?.attr("src")
val epRegex = Regex("(\\/(\\d+)\$)")
val url = it.selectFirst("a")?.attr("href")?.replace(epRegex,"")?.replace("/ver/","/donghua/")
val epnumRegex = Regex("((\\d+)$)")
val epNum = epnumRegex.find(title)?.value?.toIntOrNull()
val dubstat = if (title.contains("Latino") || title.contains("Castellano")) DubStatus.Dubbed else DubStatus.Subbed
newAnimeSearchResponse(title.replace(Regex("Episodio|(\\d+)"),"").trim(), fixUrl(url ?: "")) {
this.posterUrl = fixUrl(poster ?: "")
addDubStatus(dubstat, epNum)
}
})
)
urls.apmap { (url, name) ->
val home = app.get(url, timeout = 120).document.select(".col-xs-4").map {
val title = it.selectFirst(".fs-14")?.text() ?: ""
val poster = it.selectFirst(".fit-1 img")?.attr("src") ?: ""
AnimeSearchResponse(
title,
fixUrl(it.selectFirst("a")?.attr("href") ?: ""),
this.name,
TvType.Anime,
fixUrl(poster),
null,
if (title.contains("Latino") || title.contains("Castellano")) EnumSet.of(
DubStatus.Dubbed
) else EnumSet.of(DubStatus.Subbed),
)
}
items.add(HomePageList(name, home))
}
if (items.size <= 0) throw ErrorLoadingException()
return HomePageResponse(items)
}
override suspend fun search(query: String): List<SearchResponse> {
return app.get("$mainUrl/busquedas/$query", timeout = 120).document.select(".col-xs-4").map {
val title = it.selectFirst(".fs-14")?.text() ?: ""
val href = fixUrl(it.selectFirst("a")?.attr("href") ?: "")
val image = it.selectFirst(".fit-1 img")?.attr("src")
AnimeSearchResponse(
title,
href,
this.name,
TvType.Anime,
fixUrl(image ?: ""),
null,
if (title.contains("Latino") || title.contains("Castellano")) EnumSet.of(
DubStatus.Dubbed
) else EnumSet.of(DubStatus.Subbed),
)
}
}
override suspend fun load(url: String): LoadResponse {
val doc = app.get(url, timeout = 120).document
val poster = doc.selectFirst("head meta[property=og:image]")?.attr("content") ?: ""
val title = doc.selectFirst(".ls-title-serie")?.text() ?: ""
val description = doc.selectFirst("p.text-justify.fc-dark")?.text() ?: ""
val genres = doc.select("span.label.label-primary.f-bold").map { it.text() }
val status = when (doc.selectFirst("div.col-md-6.col-xs-6.align-center.bg-white.pt-10.pr-15.pb-0.pl-15 p span.badge.bg-default")?.text()) {
"En Emisión" -> ShowStatus.Ongoing
"Finalizada" -> ShowStatus.Completed
else -> null
}
val episodes = doc.select("ul.donghua-list a").map {
val name = it.selectFirst(".fs-16")?.text()
val link = it.attr("href")
Episode(fixUrl(link), name)
}.reversed()
val typeinfo = doc.select("div.row div.col-md-6.pl-15 p.fc-dark").text()
val tvType = if (typeinfo.contains(Regex("Tipo.*Pel.cula"))) TvType.AnimeMovie else TvType.Anime
return newAnimeLoadResponse(title, url, tvType) {
posterUrl = poster
addEpisodes(DubStatus.Subbed, episodes)
showStatus = status
plot = description
tags = genres
}
}
data class Protea (
@JsonProperty("source") val source: List<Source>,
@JsonProperty("poster") val poster: String?
)
data class Source (
@JsonProperty("file") val file: String,
@JsonProperty("label") val label: String?,
@JsonProperty("type") val type: String?,
@JsonProperty("default") val default: String?
)
private fun cleanStream(
name: String,
url: String,
qualityString: String?,
callback: (ExtractorLink) -> Unit,
isM3U8: Boolean
): Boolean {
callback(
ExtractorLink(
name,
name,
url,
"",
getQualityFromName(qualityString),
isM3U8
)
)
return true
}
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
app.get(data).document.select("script").apmap { script ->
if (script.data().contains("eval(function(p,a,c,k,e")) {
val packedRegex = Regex("eval\\(function\\(p,a,c,k,e,.*\\)\\)")
packedRegex.findAll(script.data()).map {
it.value
}.toList().apmap {
val unpack = getAndUnpack(it).replace("diasfem","embedsito")
fetchUrls(unpack).apmap { url ->
loadExtractor(url, data, subtitleCallback, callback)
}
if (unpack.contains("protea_tab")) {
val protearegex = Regex("(protea_tab.*slug.*,type)")
val slug = protearegex.findAll(unpack).map {
it.value.replace(Regex("(protea_tab.*slug\":\")"),"").replace("\"},type","")
}.first()
val requestlink = "$mainUrl/api_donghua.php?slug=$slug"
val response = app.get(requestlink, headers =
mapOf("Host" to "www.mundodonghua.com",
"User-Agent" to USER_AGENT,
"Accept" to "*/*",
"Accept-Language" to "en-US,en;q=0.5",
"Referer" to data,
"X-Requested-With" to "XMLHttpRequest",
"DNT" to "1",
"Connection" to "keep-alive",
"Sec-Fetch-Dest" to "empty",
"Sec-Fetch-Mode" to "no-cors",
"Sec-Fetch-Site" to "same-origin",
"TE" to "trailers",
"Pragma" to "no-cache",
"Cache-Control" to "no-cache",)
).text.removePrefix("[").removeSuffix("]")
val json = parseJson<Protea>(response)
json.source.forEach { source ->
val protename = "Protea"
cleanStream(protename, fixUrl(source.file), source.label, callback, false)
}
}
if (unpack.contains("asura_player")) {
val asuraRegex = Regex("(asura_player.*type)")
asuraRegex.findAll(unpack).map {
it.value
}.toList().apmap { protea ->
val asuraname = "Asura"
val file = protea.substringAfter("{file:\"").substringBefore("\"")
generateM3u8(
asuraname,
file,
""
).forEach {
cleanStream(asuraname, it.url, it.quality.toString(), callback, true)
}
}
}
}
}
}
return true
}
}

View File

@ -0,0 +1,14 @@
package com.lagradost
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
import com.lagradost.cloudstream3.plugins.Plugin
import android.content.Context
@CloudstreamPlugin
class MundoDonghuaProviderPlugin: Plugin() {
override fun load(context: Context) {
// All providers should be added in this manner. Please don't edit the providers list directly.
registerMainAPI(MundoDonghuaProvider())
}
}

View File

@ -0,0 +1,22 @@
// use an integer for version numbers
version = 1
cloudstream {
// All of these properties are optional, you can safely remove them
// description = "Lorem Ipsum"
// authors = listOf("Cloudburst")
/**
* Status int as the following:
* 0: Down
* 1: Ok
* 2: Slow
* 3: Beta only
* */
status = 1 // will be 3 if unspecified
// Set to true to get an 18+ symbol next to the plugin
adult = false // will be false if unspecified
}

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.lagradost"/>

View File

@ -0,0 +1,178 @@
package com.lagradost
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.loadExtractor
import org.jsoup.nodes.Element
import java.util.*
class NeonimeProvider : MainAPI() {
override var mainUrl = "https://neonime.watch"
override var name = "Neonime"
override val hasQuickSearch = false
override val hasMainPage = true
override var lang = "id"
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") || t.contains("Special")) TvType.OVA
else if (t.contains("Movie")) TvType.AnimeMovie
else TvType.Anime
}
fun getStatus(t: String): ShowStatus {
return when (t) {
"Ended" -> ShowStatus.Completed
"OnGoing" -> ShowStatus.Ongoing
"Ongoing" -> ShowStatus.Ongoing
"In Production" -> ShowStatus.Ongoing
"Returning Series" -> ShowStatus.Ongoing
else -> ShowStatus.Completed
}
}
}
override val mainPage = mainPageOf(
"$mainUrl/episode/page/" to "Episode Terbaru",
"$mainUrl/tvshows/page/" to "Anime Terbaru",
"$mainUrl/movies/page/" to "Movie",
)
override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse {
val document = app.get(request.data + page).document
val home = document.select("tbody tr,div.item").mapNotNull {
it.toSearchResult()
}
return newHomePageResponse(request.name, home)
}
private fun getProperAnimeLink(uri: String): String {
return when {
uri.contains("/episode") -> {
val title = uri.substringAfter("$mainUrl/episode/").let { tt ->
val fixTitle = Regex("(.*)-\\d{1,2}x\\d+").find(tt)?.groupValues?.getOrNull(1).toString()
when {
!tt.contains("-season") && !tt.contains(Regex("-1x\\d+")) && !tt.contains("one-piece") -> "$fixTitle-season-${Regex("-(\\d{1,2})x\\d+").find(tt)?.groupValues?.getOrNull(1).toString()}"
tt.contains("-special") -> fixTitle.replace(Regex("-x\\d+"), "")
!fixTitle.contains("-subtitle-indonesia") -> "$fixTitle-subtitle-indonesia"
else -> fixTitle
}
}
// title = when {
// title.contains("youkoso-jitsuryoku") && !title.contains("-season") -> title.replace("-e-", "-e-tv-")
// else -> title
// }
"$mainUrl/tvshows/$title"
}
else -> uri
}
}
private fun Element.toSearchResult(): AnimeSearchResponse? {
val title = this.selectFirst("td.bb a")?.ownText() ?: this.selectFirst("h2")?.text() ?: return null
val href = getProperAnimeLink(fixUrl(this.select("a").attr("href")))
val posterUrl = fixUrl(this.select("img").attr("data-src"))
val epNum = this.selectFirst("td.bb span")?.text()?.let { eps ->
Regex("Episode\\s?([0-9]+)").find(eps)?.groupValues?.getOrNull(1)?.toIntOrNull()
}
return newAnimeSearchResponse(title, href, TvType.Anime) {
this.posterUrl = posterUrl
addSub(epNum)
}
}
override suspend fun search(query: String): List<SearchResponse> {
val link = "$mainUrl/?s=$query"
val document = app.get(link).document
return document.select("div.item.episode-home").mapNotNull {
val title = it.selectFirst("div.judul-anime > span")!!.text()
val poster = it.select("img").attr("data-src").toString().trim()
val episodes = it.selectFirst("div.fixyear > h2.text-center")!!
.text().replace(Regex("[^0-9]"), "").trim().toIntOrNull()
val tvType = getType(it.selectFirst("span.calidad2.episode")?.text().toString())
val href = getProperAnimeLink(fixUrl(it.selectFirst("a")!!.attr("href")))
newAnimeSearchResponse(title, href, tvType) {
this.posterUrl = poster
addSub(episodes)
}
}
}
override suspend fun load(url: String): LoadResponse {
val document = app.get(url).document
if (url.contains("movie") || url.contains("live-action")) {
val mTitle = document.selectFirst(".sbox > .data > h1[itemprop = name]")?.text().toString().trim()
val mTrailer = document.selectFirst("div.youtube_id iframe")?.attr("data-wpfc-original-src")?.substringAfterLast("html#")?.let{ "https://www.youtube.com/embed/$it"}
return newMovieLoadResponse(name = mTitle, url = url, type = TvType.Movie, dataUrl = url) {
posterUrl = document.selectFirst(".sbox > .imagen > .fix > img[itemprop = image]")?.attr("data-src")
year = document.selectFirst("a[href*=release-year]")!!.text().toIntOrNull()
plot = document.select("div[itemprop = description]").text().trim()
rating = document.select("span[itemprop = ratingValue]").text().toIntOrNull()
tags = document.select("p.meta_dd > a").map { it.text() }
addTrailer(mTrailer)
}
}
else {
val title = document.select("h1[itemprop = name]").text().trim()
val trailer = document.selectFirst("div.youtube_id_tv iframe")?.attr("data-wpfc-original-src")?.substringAfterLast("html#")?.let{ "https://www.youtube.com/embed/$it"}
val episodes = document.select("ul.episodios > li").mapNotNull {
val header = it.selectFirst(".episodiotitle > a")?.ownText().toString()
val name = Regex("(Episode\\s?[0-9]+)").find(header)?.groupValues?.getOrNull(0) ?: header
val link = fixUrl(it.selectFirst(".episodiotitle > a")!!.attr("href"))
Episode(link, name)
}.reversed()
return newAnimeLoadResponse(title, url, TvType.Anime) {
engName = title
posterUrl = document.selectFirst(".imagen > img")?.attr("data-src")
year = document.select("#info a[href*=\"-year/\"]").text().toIntOrNull()
addEpisodes(DubStatus.Subbed, episodes)
showStatus = getStatus(document.select("div.metadatac > span").last()!!.text().trim())
plot = document.select("div[itemprop = description] > p").text().trim()
tags = document.select("#info a[href*=\"-genre/\"]").map { it.text() }
addTrailer(trailer)
}
}
}
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
val source = if(data.contains("movie") || data.contains("live-action")) {
app.get(data).document.select("#player2-1 > div[id*=div]").mapNotNull {
fixUrl(it.select("iframe").attr("data-src"))
}
} else {
app.get(data).document.select(".player2 > .embed2 > div[id*=player]").mapNotNull {
fixUrl(it.select("iframe").attr("data-src"))
}
}
source.apmap {
loadExtractor(it, data, subtitleCallback, callback)
}
return true
}
}

Some files were not shown because too many files have changed in this diff Show More