remove all english providers

This commit is contained in:
C10udburst 2022-08-17 14:32:36 +02:00
parent 50a0175ac3
commit 440d8ef576
145 changed files with 0 additions and 12371 deletions

View File

@ -1,25 +0,0 @@
// 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
tvTypes = listOf(
"AnimeMovie",
"Anime",
)
iconUrl = "https://www.google.com/s2/favicons?domain=allanime.site&sz=24"
}

View File

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

View File

@ -1,404 +0,0 @@
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

@ -1,14 +0,0 @@
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

@ -1,25 +0,0 @@
// 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
tvTypes = listOf(
"TvSeries",
"Movie",
)
iconUrl = "https://www.google.com/s2/favicons?domain=allmoviesforyou.net&sz=24"
}

View File

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

View File

@ -1,206 +0,0 @@
package com.lagradost
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.LoadResponse.Companion.addDuration
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.loadExtractor
import org.jsoup.Jsoup
class AllMoviesForYouProvider : MainAPI() {
companion object {
fun getType(t: String): TvType {
return when {
t.contains("series") -> TvType.TvSeries
t.contains("movies") -> TvType.Movie
else -> TvType.Movie
}
}
}
// Fetching movies will not work if this link is outdated.
override var mainUrl = "https://allmoviesforyou.net"
override var name = "AllMoviesForYou"
override val hasMainPage = true
override val supportedTypes = setOf(
TvType.Movie,
TvType.TvSeries
)
override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse {
val items = ArrayList<HomePageList>()
val soup = app.get(mainUrl).document
val urls = listOf(
Pair("Movies", "section[data-id=movies] article.TPost.B"),
Pair("TV Series", "section[data-id=series] article.TPost.B"),
)
for ((name, element) in urls) {
try {
val home = soup.select(element).map {
val title = it.selectFirst("h2.title")!!.text()
val link = it.selectFirst("a")!!.attr("href")
TvSeriesSearchResponse(
title,
link,
this.name,
TvType.Movie,
fixUrl(it.selectFirst("figure img")!!.attr("data-src")),
null,
null,
)
}
items.add(HomePageList(name, home))
} catch (e: Exception) {
logError(e)
}
}
if (items.size <= 0) throw ErrorLoadingException()
return HomePageResponse(items)
}
override suspend fun search(query: String): List<SearchResponse> {
val url = "$mainUrl/?s=$query"
val document = app.get(url).document
val items = document.select("ul.MovieList > li > article > a")
return items.map { item ->
val href = item.attr("href")
val title = item.selectFirst("> h2.Title")!!.text()
val img = fixUrl(item.selectFirst("> div.Image > figure > img")!!.attr("data-src"))
val type = getType(href)
if (type == TvType.Movie) {
MovieSearchResponse(title, href, this.name, type, img, null)
} else {
TvSeriesSearchResponse(
title,
href,
this.name,
type,
img,
null,
null
)
}
}
}
// private fun getLink(document: Document): List<String>? {
// val list = ArrayList<String>()
// Regex("iframe src=\"(.*?)\"").find(document.html())?.groupValues?.get(1)?.let {
// list.add(it)
// }
// document.select("div.OptionBx")?.forEach { element ->
// val baseElement = element.selectFirst("> a.Button")
// val elementText = element.selectFirst("> p.AAIco-dns")?.text()
// if (elementText == "Streamhub" || elementText == "Dood") {
// baseElement?.attr("href")?.let { href ->
// list.add(href)
// }
// }
// }
//
// return if (list.isEmpty()) null else list
// }
override suspend fun load(url: String): LoadResponse {
val type = getType(url)
val document = app.get(url).document
val title = document.selectFirst("h1.Title")!!.text()
val descipt = document.selectFirst("div.Description > p")!!.text()
val rating =
document.selectFirst("div.Vote > div.post-ratings > span")?.text()?.toRatingInt()
val year = document.selectFirst("span.Date")?.text()
val duration = document.selectFirst("span.Time")!!.text()
val backgroundPoster =
fixUrlNull(document.selectFirst("div.Image > figure > img")?.attr("data-src"))
if (type == TvType.TvSeries) {
val list = ArrayList<Pair<Int, String>>()
document.select("main > section.SeasonBx > div > div.Title > a").forEach { element ->
val season = element.selectFirst("> span")?.text()?.toIntOrNull()
val href = element.attr("href")
if (season != null && season > 0 && !href.isNullOrBlank()) {
list.add(Pair(season, fixUrl(href)))
}
}
if (list.isEmpty()) throw ErrorLoadingException("No Seasons Found")
val episodeList = ArrayList<Episode>()
for (season in list) {
val seasonResponse = app.get(season.second).text
val seasonDocument = Jsoup.parse(seasonResponse)
val episodes = seasonDocument.select("table > tbody > tr")
if (episodes.isNotEmpty()) {
episodes.forEach { episode ->
val epNum = episode.selectFirst("> td > span.Num")?.text()?.toIntOrNull()
val poster = episode.selectFirst("> td.MvTbImg > a > img")?.attr("data-src")
val aName = episode.selectFirst("> td.MvTbTtl > a")
val name = aName!!.text()
val href = aName.attr("href")
val date = episode.selectFirst("> td.MvTbTtl > span")?.text()
episodeList.add(
newEpisode(href) {
this.name = name
this.season = season.first
this.episode = epNum
this.posterUrl = fixUrlNull(poster)
addDate(date)
}
)
}
}
}
return TvSeriesLoadResponse(
title,
url,
this.name,
type,
episodeList,
backgroundPoster,
year?.toIntOrNull(),
descipt,
null,
rating
)
} else {
return newMovieLoadResponse(
title,
url,
type,
fixUrl(url)
) {
posterUrl = backgroundPoster
this.year = year?.toIntOrNull()
this.plot = descipt
this.rating = rating
addDuration(duration)
}
}
}
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
val doc = app.get(data).document
val iframe = doc.select("body iframe").map { fixUrl(it.attr("src")) }
iframe.apmap { id ->
if (id.contains("trembed")) {
val soup = app.get(id).document
soup.select("body iframe").map {
val link = fixUrl(it.attr("src").replace("streamhub.to/d/", "streamhub.to/e/"))
loadExtractor(link, data, subtitleCallback, callback)
}
} else loadExtractor(id, data, subtitleCallback, callback)
}
return true
}
}

View File

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

View File

@ -1,26 +0,0 @@
// 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
tvTypes = listOf(
"Anime",
"AnimeMovie",
"OVA",
)
iconUrl = "https://www.google.com/s2/favicons?domain=aniflix.pro&sz=24"
}

View File

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

View File

@ -1,274 +0,0 @@
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

@ -1,14 +0,0 @@
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

@ -1,25 +0,0 @@
// 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
tvTypes = listOf(
"AnimeMovie",
"Anime",
)
iconUrl = "https://www.google.com/s2/favicons?domain=animeflick.net&sz=24"
}

View File

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

View File

@ -1,119 +0,0 @@
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

@ -1,14 +0,0 @@
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

@ -1,25 +0,0 @@
// 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 = 0 // will be 3 if unspecified
tvTypes = listOf(
"AnimeMovie",
"Anime",
"OVA",
)
}

View File

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

View File

@ -1,562 +0,0 @@
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

@ -1,14 +0,0 @@
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

@ -1,26 +0,0 @@
// 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 = 0 // will be 3 if unspecified
tvTypes = listOf(
"AnimeMovie",
"Anime",
"OVA",
)
iconUrl = "https://www.google.com/s2/favicons?domain=animekisa.in&sz=24"
}

View File

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

View File

@ -1,131 +0,0 @@
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

@ -1,14 +0,0 @@
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

@ -1,25 +0,0 @@
// 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
tvTypes = listOf(
"AsianDrama",
"OVA",
)
iconUrl = "https://www.google.com/s2/favicons?domain=asiaflix.app&sz=24"
}

View File

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

View File

@ -1,198 +0,0 @@
package com.lagradost
import com.fasterxml.jackson.annotation.JsonProperty
import com.fasterxml.jackson.core.JsonParser
import com.fasterxml.jackson.module.kotlin.readValue
import com.lagradost.cloudstream3.*
//import com.lagradost.cloudstream3.animeproviders.GogoanimeProvider.Companion.getStatus
import com.lagradost.cloudstream3.utils.DataStore.toKotlinObject
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.getQualityFromName
import java.net.URI
class AsiaFlixProvider : 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
}
}
}
override var mainUrl = "https://asiaflix.app"
override var name = "AsiaFlix"
override val hasQuickSearch = false
override val hasMainPage = true
override val hasChromecastSupport = false
override val supportedTypes = setOf(TvType.AsianDrama)
private val apiUrl = "https://api.asiaflix.app/api/v2"
data class DashBoardObject(
@JsonProperty("sectionName") val sectionName: String,
@JsonProperty("type") val type: String?,
@JsonProperty("data") val data: List<Data>?
)
data class Episodes(
@JsonProperty("_id") val _id: String,
@JsonProperty("epUrl") val epUrl: String?,
@JsonProperty("number") val number: Int?,
@JsonProperty("type") val type: String?,
@JsonProperty("extracted") val extracted: String?,
@JsonProperty("videoUrl") val videoUrl: String?
)
data class Data(
@JsonProperty("_id") val _id: String,
@JsonProperty("name") val name: String,
@JsonProperty("altNames") val altNames: String?,
@JsonProperty("image") val image: String?,
@JsonProperty("tvStatus") val tvStatus: String?,
@JsonProperty("genre") val genre: String?,
@JsonProperty("releaseYear") val releaseYear: Int?,
@JsonProperty("createdAt") val createdAt: Long?,
@JsonProperty("episodes") val episodes: List<Episodes>?,
@JsonProperty("views") val views: Int?
)
data class DramaPage(
@JsonProperty("_id") val _id: String,
@JsonProperty("name") val name: String,
@JsonProperty("altNames") val altNames: String?,
@JsonProperty("synopsis") val synopsis: String?,
@JsonProperty("image") val image: String?,
@JsonProperty("language") val language: String?,
@JsonProperty("dramaUrl") val dramaUrl: String?,
@JsonProperty("published") val published: Boolean?,
@JsonProperty("tvStatus") val tvStatus: String?,
@JsonProperty("firstAirDate") val firstAirDate: String?,
@JsonProperty("genre") val genre: String?,
@JsonProperty("releaseYear") val releaseYear: Int?,
@JsonProperty("createdAt") val createdAt: Long?,
@JsonProperty("modifiedAt") val modifiedAt: Long?,
@JsonProperty("episodes") val episodes: List<Episodes>,
@JsonProperty("__v") val __v: Int?,
@JsonProperty("cdnImage") val cdnImage: String?,
@JsonProperty("views") val views: Int?
)
private fun Data.toSearchResponse(): TvSeriesSearchResponse {
return TvSeriesSearchResponse(
name,
_id,
this@AsiaFlixProvider.name,
TvType.AsianDrama,
image,
releaseYear,
episodes?.size,
)
}
private fun Episodes.toEpisode(): Episode? {
if (videoUrl != null && videoUrl.contains("watch/null") || number == null) return null
return videoUrl?.let {
Episode(
it,
null,
number,
)
}
}
private fun DramaPage.toLoadResponse(): TvSeriesLoadResponse {
return TvSeriesLoadResponse(
name,
"$mainUrl$dramaUrl/$_id".replace("drama-detail", "show-details"),
this@AsiaFlixProvider.name,
TvType.AsianDrama,
episodes.mapNotNull { it.toEpisode() }.sortedBy { it.episode },
image,
releaseYear,
synopsis,
getStatus(tvStatus ?: ""),
null,
genre?.split(",")?.map { it.trim() }
)
}
override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse {
val headers = mapOf("X-Requested-By" to "asiaflix-web")
val response = app.get("$apiUrl/dashboard", headers = headers).text
val customMapper =
mapper.copy().configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true)
// Hack, because it can either be object or a list
val cleanedResponse = Regex(""""data":(\{.*?),\{"sectionName"""").replace(response) {
""""data":null},{"sectionName""""
}
val dashBoard = customMapper.readValue<List<DashBoardObject>?>(cleanedResponse)
val listItems = dashBoard?.mapNotNull {
it.data?.map { data ->
data.toSearchResponse()
}?.let { searchResponse ->
HomePageList(it.sectionName, searchResponse)
}
}
return HomePageResponse(listItems ?: listOf())
}
data class Link(
@JsonProperty("url") val url: String?,
)
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
if (isCasting) return false
val headers = mapOf("X-Requested-By" to "asiaflix-web")
app.get(
"$apiUrl/utility/get-stream-links?url=$data",
headers = headers
).text.toKotlinObject<Link>().url?.let {
// val fixedUrl = "https://api.asiaflix.app/api/v2/utility/cors-proxy/playlist/${URLEncoder.encode(it, StandardCharsets.UTF_8.toString())}"
callback.invoke(
ExtractorLink(
name,
name,
it,
"https://asianload1.com/",
/** <------ This provider should be added instead */
getQualityFromName(it),
URI(it).path.endsWith(".m3u8")
)
)
}
return true
}
override suspend fun search(query: String): List<SearchResponse>? {
val headers = mapOf("X-Requested-By" to "asiaflix-web")
val url = "$apiUrl/drama/search?q=$query"
val response = app.get(url, headers = headers).text
return mapper.readValue<List<Data>?>(response)?.map { it.toSearchResponse() }
}
override suspend fun load(url: String): LoadResponse {
val headers = mapOf("X-Requested-By" to "asiaflix-web")
val requestUrl = "$apiUrl/drama?id=${url.split("/").lastOrNull()}"
val response = app.get(requestUrl, headers = headers).text
val dramaPage = response.toKotlinObject<DramaPage>()
return dramaPage.toLoadResponse()
}
}

View File

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

View File

@ -1,22 +0,0 @@
// 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
iconUrl = "https://www.google.com/s2/favicons?domain=bflix.ru&sz=24"
}

View File

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

View File

@ -1,300 +0,0 @@
package com.lagradost
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.*
import com.lagradost.NineAnimeApi.decodeVrf
import com.lagradost.NineAnimeApi.encode
import com.lagradost.NineAnimeApi.encodeVrf
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.loadExtractor
import org.jsoup.Jsoup
open class BflixProvider : MainAPI() {
override var mainUrl = "https://bflix.ru"
override var name = "Bflix"
override val hasMainPage = true
override val hasChromecastSupport = true
override val hasDownloadSupport = true
override val supportedTypes = setOf(
TvType.Movie,
TvType.TvSeries,
)
//override val uniqueId: Int by lazy { "BflixProvider".hashCode() }
override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse {
val items = ArrayList<HomePageList>()
val soup = app.get("$mainUrl/home").document
val testa = listOf(
Pair("Movies", "div.tab-content[data-name=movies] div.filmlist div.item"),
Pair("Shows", "div.tab-content[data-name=shows] div.filmlist div.item"),
Pair("Trending", "div.tab-content[data-name=trending] div.filmlist div.item"),
Pair(
"Latest Movies",
"div.container section.bl:contains(Latest Movies) div.filmlist div.item"
),
Pair(
"Latest TV-Series",
"div.container section.bl:contains(Latest TV-Series) div.filmlist div.item"
),
)
for ((name, element) in testa) try {
val test = soup.select(element).map {
val title = it.selectFirst("h3 a")!!.text()
val link = fixUrl(it.selectFirst("a")!!.attr("href"))
val qualityInfo = it.selectFirst("div.quality")!!.text()
val quality = getQualityFromString(qualityInfo)
TvSeriesSearchResponse(
title,
link,
this.name,
if (link.contains("/movie/")) TvType.Movie else TvType.TvSeries,
it.selectFirst("a.poster img")!!.attr("src"),
null,
null,
quality = quality
)
}
items.add(HomePageList(name, test))
} catch (e: Exception) {
e.printStackTrace()
}
if (items.size <= 0) throw ErrorLoadingException()
return HomePageResponse(items)
}
override suspend fun search(query: String): List<SearchResponse>? {
val encodedquery = encodeVrf(query, mainKey)
val url = "$mainUrl/search?keyword=$query&vrf=$encodedquery"
val html = app.get(url).text
val document = Jsoup.parse(html)
return document.select(".filmlist div.item").map {
val title = it.selectFirst("h3 a")!!.text()
val href = fixUrl(it.selectFirst("a")!!.attr("href"))
val image = it.selectFirst("a.poster img")!!.attr("src")
val isMovie = href.contains("/movie/")
val qualityInfo = it.selectFirst("div.quality")!!.text()
val quality = getQualityFromString(qualityInfo)
if (isMovie) {
MovieSearchResponse(
title,
href,
this.name,
TvType.Movie,
image,
null,
quality = quality
)
} else {
TvSeriesSearchResponse(
title,
href,
this.name,
TvType.TvSeries,
image,
null,
null,
quality = quality
)
}
}
}
data class Response(
@JsonProperty("html") val html: String
)
companion object {
val mainKey = "OrAimkpzm6phmN3j"
}
override suspend fun load(url: String): LoadResponse? {
val soup = app.get(url).document
val movieid = soup.selectFirst("div#watch")!!.attr("data-id")
val movieidencoded = encodeVrf(movieid, mainKey)
val title = soup.selectFirst("div.info h1")!!.text()
val description = soup.selectFirst(".info .desc")?.text()?.trim()
val poster: String? = try {
soup.selectFirst("img.poster")!!.attr("src")
} catch (e: Exception) {
soup.selectFirst(".info .poster img")!!.attr("src")
}
val tags = soup.select("div.info .meta div:contains(Genre) a").map { it.text() }
val vrfUrl = "$mainUrl/ajax/film/servers?id=$movieid&vrf=$movieidencoded"
println("VRF___ $vrfUrl")
val episodes = Jsoup.parse(
app.get(
vrfUrl
).parsed<Response>().html
).select("div.episode").map {
val a = it.selectFirst("a")
val href = fixUrl(a!!.attr("href"))
val extraData = a.attr("data-kname").let { str ->
str.split("-").mapNotNull { subStr -> subStr.toIntOrNull() }
}
val isValid = extraData.size == 2
val episode = if (isValid) extraData.getOrNull(1) else null
val season = if (isValid) extraData.getOrNull(0) else null
val eptitle = it.selectFirst(".episode a span.name")!!.text()
val secondtitle = it.selectFirst(".episode a span")!!.text()
.replace(Regex("(Episode (\\d+):|Episode (\\d+)-|Episode (\\d+))"), "") ?: ""
Episode(
href,
secondtitle + eptitle,
season,
episode,
)
}
val tvType =
if (url.contains("/movie/") && episodes.size == 1) TvType.Movie else TvType.TvSeries
val recommendations =
soup.select("div.bl-2 section.bl div.content div.filmlist div.item")
.mapNotNull { element ->
val recTitle = element.select("h3 a").text() ?: return@mapNotNull null
val image = element.select("a.poster img")?.attr("src")
val recUrl = fixUrl(element.select("a").attr("href"))
MovieSearchResponse(
recTitle,
recUrl,
this.name,
if (recUrl.contains("/movie/")) TvType.Movie else TvType.TvSeries,
image,
year = null
)
}
val rating = soup.selectFirst(".info span.imdb")?.text()?.toRatingInt()
val durationdoc = soup.selectFirst("div.info div.meta").toString()
val durationregex = Regex("((\\d+) min)")
val yearegex = Regex("<span>(\\d+)</span>")
val duration = if (durationdoc.contains("na min")) null
else durationregex.find(durationdoc)?.destructured?.component1()?.replace(" min", "")
?.toIntOrNull()
val year = if (mainUrl == "https://bflix.ru") {
yearegex.find(durationdoc)?.destructured?.component1()
?.replace(Regex("<span>|</span>"), "")
} else null
return when (tvType) {
TvType.TvSeries -> {
TvSeriesLoadResponse(
title,
url,
this.name,
tvType,
episodes,
poster,
year?.toIntOrNull(),
description,
null,
rating,
tags,
recommendations = recommendations,
duration = duration,
)
}
TvType.Movie -> {
MovieLoadResponse(
title,
url,
this.name,
tvType,
url,
poster,
year?.toIntOrNull(),
description,
rating,
tags,
recommendations = recommendations,
duration = duration
)
}
else -> null
}
}
data class Subtitles(
@JsonProperty("file") val file: String,
@JsonProperty("label") val label: String,
@JsonProperty("kind") val kind: String
)
data class Links(
@JsonProperty("url") val url: String
)
data class Servers(
@JsonProperty("28") val mcloud: String?,
@JsonProperty("35") val mp4upload: String?,
@JsonProperty("40") val streamtape: String?,
@JsonProperty("41") val vidstream: String?,
@JsonProperty("43") val videovard: String?
)
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
val soup = app.get(data).document
val movieid = encode(soup.selectFirst("div#watch")?.attr("data-id") ?: return false)
val movieidencoded = encodeVrf(movieid, mainKey)
Jsoup.parse(
parseJson<Response>(
app.get(
"$mainUrl/ajax/film/servers?id=$movieid&vrf=$movieidencoded"
).text
).html
)
.select("html body #episodes").map {
val cleandata = data.replace(mainUrl, "")
val a = it.select("a").map {
it.attr("data-kname")
}
val tvType =
if (data.contains("movie/") && a.size == 1) TvType.Movie else TvType.TvSeries
val servers = if (tvType == TvType.Movie) it.select(".episode a").attr("data-ep")
else
it.select(".episode a[href=$cleandata]").attr("data-ep")
?: it.select(".episode a[href=${cleandata.replace("/1-full", "")}]")
.attr("data-ep")
val jsonservers = parseJson<Servers?>(servers) ?: return@map
listOfNotNull(
jsonservers.vidstream,
jsonservers.mcloud,
jsonservers.mp4upload,
jsonservers.streamtape,
jsonservers.videovard,
).mapNotNull {
val epserver = app.get("$mainUrl/ajax/episode/info?id=$it").text
(if (epserver.contains("url")) {
parseJson<Links>(epserver)
} else null)?.url?.let {
decodeVrf(it, mainKey)
}
}.apmap { url ->
loadExtractor(
url, data, subtitleCallback, callback
)
}
//Apparently any server works, I haven't found any diference
val sublink =
app.get("$mainUrl/ajax/episode/subtitles/${jsonservers.mcloud}").text
val jsonsub = parseJson<List<Subtitles>>(sublink)
jsonsub.forEach { subtitle ->
subtitleCallback(
SubtitleFile(subtitle.label, subtitle.file)
)
}
}
return true
}
}

View File

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

View File

@ -1,6 +0,0 @@
package com.lagradost
class FmoviesToProvider : BflixProvider() {
override var mainUrl = "https://fmovies.to"
override var name = "Fmovies.to"
}

View File

@ -1,112 +0,0 @@
package com.lagradost
// taken from https://github.com/saikou-app/saikou/blob/b35364c8c2a00364178a472fccf1ab72f09815b4/app/src/main/java/ani/saikou/parsers/anime/NineAnime.kt
// GNU General Public License v3.0 https://github.com/saikou-app/saikou/blob/main/LICENSE.md
object NineAnimeApi {
private const val nineAnimeKey =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
private const val cipherKey = "kMXzgyNzT3k5dYab"
fun encodeVrf(text: String, mainKey: String): String {
return encode(
encrypt(
cipher(mainKey, encode(text)),
nineAnimeKey
)//.replace("""=+$""".toRegex(), "")
)
}
fun decodeVrf(text: String, mainKey: String): String {
return decode(cipher(mainKey, decrypt(text, nineAnimeKey)))
}
fun encrypt(input: String, key: String): String {
if (input.any { it.code > 255 }) throw Exception("illegal characters!")
var output = ""
for (i in input.indices step 3) {
val a = intArrayOf(-1, -1, -1, -1)
a[0] = input[i].code shr 2
a[1] = (3 and input[i].code) shl 4
if (input.length > i + 1) {
a[1] = a[1] or (input[i + 1].code shr 4)
a[2] = (15 and input[i + 1].code) shl 2
}
if (input.length > i + 2) {
a[2] = a[2] or (input[i + 2].code shr 6)
a[3] = 63 and input[i + 2].code
}
for (n in a) {
if (n == -1) output += "="
else {
if (n in 0..63) output += key[n]
}
}
}
return output
}
fun cipher(key: String, text: String): String {
val arr = IntArray(256) { it }
var u = 0
var r: Int
arr.indices.forEach {
u = (u + arr[it] + key[it % key.length].code) % 256
r = arr[it]
arr[it] = arr[u]
arr[u] = r
}
u = 0
var c = 0
return text.indices.map { j ->
c = (c + 1) % 256
u = (u + arr[c]) % 256
r = arr[c]
arr[c] = arr[u]
arr[u] = r
(text[j].code xor arr[(arr[c] + arr[u]) % 256]).toChar()
}.joinToString("")
}
@Suppress("SameParameterValue")
private fun decrypt(input: String, key: String): String {
val t = if (input.replace("""[\t\n\f\r]""".toRegex(), "").length % 4 == 0) {
input.replace("""==?$""".toRegex(), "")
} else input
if (t.length % 4 == 1 || t.contains("""[^+/0-9A-Za-z]""".toRegex())) throw Exception("bad input")
var i: Int
var r = ""
var e = 0
var u = 0
for (o in t.indices) {
e = e shl 6
i = key.indexOf(t[o])
e = e or i
u += 6
if (24 == u) {
r += ((16711680 and e) shr 16).toChar()
r += ((65280 and e) shr 8).toChar()
r += (255 and e).toChar()
e = 0
u = 0
}
}
return if (12 == u) {
e = e shr 4
r + e.toChar()
} else {
if (18 == u) {
e = e shr 2
r += ((65280 and e) shr 8).toChar()
r += (255 and e).toChar()
}
r
}
}
fun encode(input: String): String =
java.net.URLEncoder.encode(input, "utf-8").replace("+", "%20")
private fun decode(input: String): String = java.net.URLDecoder.decode(input, "utf-8")
}

View File

@ -1,6 +0,0 @@
package com.lagradost
class SflixProProvider : BflixProvider() {
override var mainUrl = "https://sflix.pro"
override var name = "Sflix.pro"
}

View File

@ -1,26 +0,0 @@
// 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
tvTypes = listOf(
"AnimeMovie",
"Anime",
)
iconUrl = "https://www.google.com/s2/favicons?domain=bestdubbedanime.com&sz=24"
}

View File

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

View File

@ -1,270 +0,0 @@
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

@ -1,14 +0,0 @@
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

@ -1,20 +0,0 @@
// 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
tvTypes = listOf("Live")
}

View File

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

View File

@ -1,120 +0,0 @@
package com.lagradost
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
import com.lagradost.cloudstream3.utils.AppUtils.toJson
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.Qualities
import org.jsoup.nodes.Element
class EjaTv : MainAPI() {
override var mainUrl = "https://eja.tv"
override var name = "Eja.tv"
// Universal language?
override var lang = "en"
override val hasDownloadSupport = false
override val hasMainPage = true
override val supportedTypes = setOf(
TvType.Live
)
private fun Element.toSearchResponse(): LiveSearchResponse? {
val link = this.select("div.alternative a").last() ?: return null
val href = fixUrl(link.attr("href"))
val img = this.selectFirst("div.thumb img")
val lang = this.selectFirst(".card-title > a")?.attr("href")?.removePrefix("?country=")
?.replace("int", "eu") //international -> European Union 🇪🇺
return LiveSearchResponse(
// Kinda hack way to get the title
img?.attr("alt")?.replaceFirst("Watch ", "") ?: return null,
href,
this@EjaTv.name,
TvType.Live,
fixUrl(img.attr("src")),
lang = lang
)
}
override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse {
// Maybe this based on app language or as setting?
val language = "English"
val dataMap = mapOf(
"News" to mapOf("language" to language, "category" to "News"),
"Sports" to mapOf("language" to language, "category" to "Sports"),
"Entertainment" to mapOf("language" to language, "category" to "Entertainment")
)
return HomePageResponse(dataMap.apmap { (title, data) ->
val document = app.post(mainUrl, data = data).document
val shows = document.select("div.card-body").mapNotNull {
it.toSearchResponse()
}
HomePageList(
title,
shows,
isHorizontalImages = true
)
})
}
override suspend fun search(query: String): List<SearchResponse> {
return app.post(
mainUrl, data = mapOf("search" to query)
).document.select("div.card-body").mapNotNull {
it.toSearchResponse()
}
}
override suspend fun load(url: String): LoadResponse {
val doc = app.get(url).document
val sections =
doc.select("li.list-group-item.d-flex.justify-content-between.align-items-center")
val link = fixUrl(sections.last()!!.select("a").attr("href"))
val title = doc.select("h5.text-center").text()
val poster = fixUrl(doc.select("p.text-center img").attr("src"))
val summary = sections.subList(0, 3).joinToString("<br>") {
val innerText = it.ownText().trim()
val outerText = it.select("a").text().trim()
"$innerText: $outerText"
}
return LiveStreamLoadResponse(
title,
url,
this.name,
LoadData(link, title).toJson(),
poster,
plot = summary
)
}
data class LoadData(
val url: String,
val title: String
)
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
val loadData = parseJson<LoadData>(data)
callback.invoke(
ExtractorLink(
this.name,
loadData.title,
loadData.url,
"",
Qualities.Unknown.value,
isM3u8 = true
)
)
return true
}
}

View File

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

View File

@ -1,26 +0,0 @@
// 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
tvTypes = listOf(
"AnimeMovie",
"Anime",
"OVA",
)
iconUrl = "https://www.google.com/s2/favicons?domain=gogoanime.lu&sz=24"
}

View File

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

View File

@ -1,412 +0,0 @@
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

@ -1,14 +0,0 @@
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

@ -1,23 +0,0 @@
// 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
tvTypes = listOf(
"Movie",
)
}

View File

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

View File

@ -1,116 +0,0 @@
package com.lagradost
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.Qualities
import org.jsoup.Jsoup
class HDMProvider : MainAPI() {
override var name = "HD Movies"
override var mainUrl = "https://hdm.to"
override val hasMainPage = true
override val supportedTypes = setOf(
TvType.Movie,
)
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.col-md-2 > article > a")
if (items.isEmpty()) return emptyList()
return items.map { i ->
val href = i.attr("href")
val data = i.selectFirst("> div.item")!!
val img = data.selectFirst("> img")!!.attr("src")
val name = data.selectFirst("> div.movie-details")!!.text()
MovieSearchResponse(name, href, this.name, TvType.Movie, img, null)
}
}
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
if (data == "") return false
val slug = Regex(".*/(.*?)\\.mp4").find(data)?.groupValues?.get(1) ?: return false
val response = app.get(data).text
val key = Regex("playlist\\.m3u8(.*?)\"").find(response)?.groupValues?.get(1) ?: return false
callback.invoke(
ExtractorLink(
this.name,
this.name,
"https://hls.1o.to/vod/$slug/playlist.m3u8$key",
"",
Qualities.P720.value,
true
)
)
return true
}
override suspend fun load(url: String): LoadResponse? {
val response = app.get(url).text
val document = Jsoup.parse(response)
val title = document.selectFirst("h2.movieTitle")?.text() ?: throw ErrorLoadingException("No Data Found")
val poster = document.selectFirst("div.post-thumbnail > img")!!.attr("src")
val descript = document.selectFirst("div.synopsis > p")!!.text()
val year = document.select("div.movieInfoAll > div.row > div.col-md-6").getOrNull(1)?.selectFirst("> p > a")?.text()
?.toIntOrNull()
val data = "src/player/\\?v=(.*?)\"".toRegex().find(response)?.groupValues?.get(1) ?: return null
return MovieLoadResponse(
title, url, this.name, TvType.Movie,
"$mainUrl/src/player/?v=$data", poster, year, descript, null
)
}
override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse {
val html = app.get(mainUrl, timeout = 25).text
val document = Jsoup.parse(html)
val all = ArrayList<HomePageList>()
val mainbody = document.getElementsByTag("body")
?.select("div.homeContentOuter > section > div.container > div")
// Fetch row title
val inner = mainbody?.select("div.col-md-2.col-sm-2.mrgb")
val title = mainbody?.select("div > div")?.firstOrNull()?.select("div.title.titleBar")?.text() ?: "Unnamed Row"
// Fetch list of items and map
if (inner != null) {
val elements: List<SearchResponse> = inner.map {
val aa = it.select("a").firstOrNull()
val item = aa?.select("div.item")
val href = aa?.attr("href")
val link = when (href != null) {
true -> fixUrl(href)
false -> ""
}
val name = item?.select("div.movie-details")?.text() ?: "<No Title>"
var image = item?.select("img")?.get(1)?.attr("src") ?: ""
val year = null
MovieSearchResponse(
name,
link,
this.name,
TvType.Movie,
image,
year,
null,
)
}
all.add(
HomePageList(
title, elements
)
)
}
return HomePageResponse(all)
}
}

View File

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

View File

@ -1,26 +0,0 @@
// 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
tvTypes = listOf(
"TvSeries",
"Movie",
"Documentary",
)
iconUrl = "https://www.google.com/s2/favicons?domain=ihavenotv.com&sz=24"
}

View File

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

View File

@ -1,222 +0,0 @@
package com.lagradost
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.loadExtractor
import org.jsoup.Jsoup
import java.net.URLEncoder
class IHaveNoTvProvider : MainAPI() {
override var mainUrl = "https://ihavenotv.com"
override var name = "I Have No TV"
override val hasQuickSearch = false
override val hasMainPage = true
override val supportedTypes = setOf(TvType.Documentary)
override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse {
// Uhh, I am too lazy to scrape the "latest documentaries" and "recommended documentaries",
// so I am just scraping 3 random categories
val allCategories = listOf(
"astronomy",
"brain",
"creativity",
"design",
"economics",
"environment",
"health",
"history",
"lifehack",
"math",
"music",
"nature",
"people",
"physics",
"science",
"technology",
"travel"
)
val categories = allCategories.asSequence().shuffled().take(3)
.toList() // randomly get 3 categories, because there are too many
val items = ArrayList<HomePageList>()
categories.forEach { cat ->
val link = "$mainUrl/category/$cat"
val html = app.get(link).text
val soup = Jsoup.parse(html)
val searchResults: MutableMap<String, SearchResponse> = mutableMapOf()
soup.select(".episodesDiv .episode").forEach { res ->
val poster = res.selectFirst("img")?.attr("src")
val aTag = if (res.html().contains("/series/")) {
res.selectFirst(".episodeMeta > a")
} else {
res.selectFirst("a[href][title]")
}
val year = Regex("""•?\s+(\d{4})\s+•""").find(
res.selectFirst(".episodeMeta")!!.text()
)?.destructured?.component1()?.toIntOrNull()
val title = aTag!!.attr("title")
val href = fixUrl(aTag.attr("href"))
searchResults[href] = TvSeriesSearchResponse(
title,
href,
this.name,
TvType.Documentary,//if (href.contains("/series/")) TvType.TvSeries else TvType.Movie,
poster,
year,
null
)
}
items.add(
HomePageList(
capitalizeString(cat),
ArrayList(searchResults.values).subList(0, 5)
)
) // just 5 results per category, app crashes when they are too many
}
return HomePageResponse(items)
}
override suspend fun search(query: String): ArrayList<SearchResponse> {
val url = """$mainUrl/search/${URLEncoder.encode(query, "UTF-8")}"""
val response = app.get(url).text
val soup = Jsoup.parse(response)
val searchResults: MutableMap<String, SearchResponse> = mutableMapOf()
soup.select(".episodesDiv .episode").forEach { res ->
val poster = res.selectFirst("img")?.attr("src")
val aTag = if (res.html().contains("/series/")) {
res.selectFirst(".episodeMeta > a")
} else {
res.selectFirst("a[href][title]")
}
val year =
Regex("""•?\s+(\d{4})\s+•""").find(
res.selectFirst(".episodeMeta")!!.text()
)?.destructured?.component1()
?.toIntOrNull()
val title = aTag!!.attr("title")
val href = fixUrl(aTag.attr("href"))
searchResults[href] = TvSeriesSearchResponse(
title,
href,
this.name,
TvType.Documentary, //if (href.contains("/series/")) TvType.TvSeries else TvType.Movie,
poster,
year,
null
)
}
return ArrayList(searchResults.values)
}
override suspend fun load(url: String): LoadResponse {
val isSeries = url.contains("/series/")
val html = app.get(url).text
val soup = Jsoup.parse(html)
val container = soup.selectFirst(".container-fluid h1")?.parent()
val title = if (isSeries) {
container?.selectFirst("h1")?.text()?.split("")?.firstOrNull().toString()
} else soup.selectFirst(".videoDetails")!!.selectFirst("strong")?.text().toString()
val description = if (isSeries) {
container?.selectFirst("p")?.text()
} else {
soup.selectFirst(".videoDetails > p")?.text()
}
var year: Int? = null
val categories: MutableSet<String> = mutableSetOf()
val episodes = if (isSeries) {
container?.select(".episode")?.map { ep ->
val thumb = ep.selectFirst("img")!!.attr("src")
val epLink = fixUrl(ep.selectFirst("a[title]")!!.attr("href"))
val (season, epNum) = if (ep.selectFirst(".episodeMeta > strong") != null &&
ep.selectFirst(".episodeMeta > strong")!!.html().contains("S")
) {
val split = ep.selectFirst(".episodeMeta > strong")?.text()?.split("E")
Pair(
split?.firstOrNull()?.replace("S", "")?.toIntOrNull(),
split?.get(1)?.toIntOrNull()
)
} else Pair<Int?, Int?>(null, null)
year = Regex("""•?\s+(\d{4})\s+•""").find(
ep.selectFirst(".episodeMeta")!!.text()
)?.destructured?.component1()?.toIntOrNull()
categories.addAll(
ep.select(".episodeMeta > a[href*=\"/category/\"]").map { it.text().trim() })
newEpisode(epLink) {
this.name = ep.selectFirst("a[title]")!!.attr("title")
this.season = season
this.episode = epNum
this.posterUrl = thumb
this.description = ep.selectFirst(".episodeSynopsis")?.text()
}
}
} else {
listOf(MovieLoadResponse(
title,
url,
this.name,
TvType.Movie,
url,
soup.selectFirst("[rel=\"image_src\"]")!!.attr("href"),
Regex("""•?\s+(\d{4})\s+•""").find(
soup.selectFirst(".videoDetails")!!.text()
)?.destructured?.component1()?.toIntOrNull(),
description,
null,
soup.selectFirst(".videoDetails")!!.select("a[href*=\"/category/\"]")
.map { it.text().trim() }
))
}
val poster = episodes?.firstOrNull().let {
if (isSeries && it != null) (it as Episode).posterUrl
else null
}
return if (isSeries) TvSeriesLoadResponse(
title,
url,
this.name,
TvType.TvSeries,
episodes!!.map { it as Episode },
poster,
year,
description,
null,
null,
categories.toList()
) else (episodes?.first() as MovieLoadResponse)
}
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
val html = app.get(data).text
val soup = Jsoup.parse(html)
val iframe = soup.selectFirst("#videoWrap iframe")
if (iframe != null) {
loadExtractor(iframe.attr("src"), null, subtitleCallback, callback)
}
return true
}
}

View File

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

View File

@ -1,25 +0,0 @@
// 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
tvTypes = listOf(
"AnimeMovie",
"Anime",
)
iconUrl = "https://www.google.com/s2/favicons?domain=kawaiifu.com&sz=24"
}

View File

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

View File

@ -1,174 +0,0 @@
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

@ -1,14 +0,0 @@
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

@ -1,24 +0,0 @@
// 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
tvTypes = listOf(
"Cartoon",
)
iconUrl = "https://www.google.com/s2/favicons?domain=kimcartoon.li&sz=24"
}

View File

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

View File

@ -1,152 +0,0 @@
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

@ -1,14 +0,0 @@
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

@ -1,27 +0,0 @@
// 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
tvTypes = listOf(
"AsianDrama",
"TvSeries",
"Anime",
"Movie",
)
iconUrl = "https://www.google.com/s2/favicons?domain=kisskh.me&sz=24"
}

View File

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

View File

@ -1,208 +0,0 @@
package com.lagradost
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.mvvm.safeApiCall
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
import com.lagradost.cloudstream3.utils.AppUtils.toJson
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.M3u8Helper
import com.lagradost.cloudstream3.utils.loadExtractor
import java.util.ArrayList
class KisskhProvider : MainAPI() {
override var mainUrl = "https://kisskh.me"
override var name = "Kisskh"
override val hasMainPage = true
override val hasDownloadSupport = true
override val supportedTypes = setOf(
TvType.AsianDrama,
TvType.Anime
)
override val mainPage = mainPageOf(
"&type=2&sub=0&country=2&status=0&order=1" to "Movie Popular",
"&type=2&sub=0&country=2&status=0&order=2" to "Movie Last Update",
"&type=1&sub=0&country=2&status=0&order=1" to "TVSeries Popular",
"&type=1&sub=0&country=2&status=0&order=2" to "TVSeries Last Update",
"&type=3&sub=0&country=0&status=0&order=1" to "Anime Popular",
"&type=3&sub=0&country=0&status=0&order=2" to "Anime Last Update",
)
override suspend fun getMainPage(
page: Int,
request: MainPageRequest
): HomePageResponse {
val home = app.get("$mainUrl/api/DramaList/List?page=$page${request.data}")
.parsedSafe<Responses>()?.data
?.mapNotNull { media ->
media.toSearchResponse()
} ?: throw ErrorLoadingException("Invalid Json reponse")
return newHomePageResponse(request.name, home)
}
private fun Media.toSearchResponse(): SearchResponse? {
return newAnimeSearchResponse(
title ?: return null,
"$title/$id",
TvType.TvSeries,
) {
this.posterUrl = thumbnail
addSub(episodesCount)
}
}
override suspend fun search(query: String): List<SearchResponse> {
val searchResponse =
app.get("$mainUrl/api/DramaList/Search?q=$query&type=0", referer = "$mainUrl/").text
return tryParseJson<ArrayList<Media>>(searchResponse)?.mapNotNull { media ->
media.toSearchResponse()
} ?: throw ErrorLoadingException("Invalid Json reponse")
}
private fun getTitle(str: String): String {
return str.replace(Regex("[^a-zA-Z0-9]"), "-")
}
override suspend fun load(url: String): LoadResponse? {
val id = url.split("/")
val res = app.get(
"$mainUrl/api/DramaList/Drama/${id.last()}?isq=false",
referer = "$mainUrl/Drama/${
getTitle(id.first())
}?id=${id.last()}"
).parsedSafe<MediaDetail>()
?: throw ErrorLoadingException("Invalid Json reponse")
val episodes = res.episodes?.map { eps ->
Episode(
data = Data(res.title, eps.number, res.id, eps.id).toJson(),
episode = eps.number
)
} ?: throw ErrorLoadingException("No Episode")
return newTvSeriesLoadResponse(
res.title ?: return null,
url,
if (res.type == "Movie" || episodes.size == 1) TvType.Movie else TvType.TvSeries,
episodes
) {
this.posterUrl = res.thumbnail
this.year = res.releaseDate?.split("-")?.first()?.toIntOrNull()
this.plot = res.description
this.tags = listOf("${res.country}", "${res.status}", "${res.type}")
this.showStatus = when (res.status) {
"Completed" -> ShowStatus.Completed
"Ongoing" -> ShowStatus.Ongoing
else -> null
}
}
}
private fun getLanguage(str: String): String {
return when (str) {
"Indonesia" -> "Indonesian"
else -> str
}
}
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
val loadData = parseJson<Data>(data)
app.get(
"$mainUrl/api/DramaList/Episode/${loadData.epsId}.png?err=false&ts=&time=",
referer = "$mainUrl/Drama/${getTitle("${loadData.title}")}/Episode-${loadData.eps}?id=${loadData.id}&ep=${loadData.epsId}&page=0&pageSize=100"
).parsedSafe<Sources>()?.let { source ->
listOf(source.video, source.thirdParty).apmap { link ->
safeApiCall {
if (link?.contains(".m3u8") == true) {
M3u8Helper.generateM3u8(
this.name,
link,
referer = "$mainUrl/",
headers = mapOf("Origin" to mainUrl)
).forEach(callback)
} else {
loadExtractor(
link?.substringBefore("=http") ?: return@safeApiCall,
"$mainUrl/",
subtitleCallback,
callback
)
}
}
}
}
// parsedSafe doesn't work in <List<Object>>
app.get("$mainUrl/api/Sub/${loadData.epsId}").text.let { res ->
tryParseJson<List<Subtitle>>(res)?.map { sub ->
subtitleCallback.invoke(
SubtitleFile(
getLanguage(sub.label ?: return@map),
sub.src ?: return@map
)
)
}
}
return true
}
data class Data(
val title: String?,
val eps: Int?,
val id: Int?,
val epsId: Int?,
)
data class Sources(
@JsonProperty("Video") val video: String?,
@JsonProperty("ThirdParty") val thirdParty: String?,
)
data class Subtitle(
@JsonProperty("src") val src: String?,
@JsonProperty("label") val label: String?,
)
data class Responses(
@JsonProperty("data") val data: ArrayList<Media>? = arrayListOf(),
)
data class Media(
@JsonProperty("episodesCount") val episodesCount: Int?,
@JsonProperty("thumbnail") val thumbnail: String?,
@JsonProperty("id") val id: Int?,
@JsonProperty("title") val title: String?,
)
data class Episodes(
@JsonProperty("id") val id: Int?,
@JsonProperty("number") val number: Int?,
@JsonProperty("sub") val sub: Int?,
)
data class MediaDetail(
@JsonProperty("description") val description: String?,
@JsonProperty("releaseDate") val releaseDate: String?,
@JsonProperty("status") val status: String?,
@JsonProperty("type") val type: String?,
@JsonProperty("country") val country: String?,
@JsonProperty("episodes") val episodes: ArrayList<Episodes>? = arrayListOf(),
@JsonProperty("thumbnail") val thumbnail: String?,
@JsonProperty("id") val id: Int?,
@JsonProperty("title") val title: String?,
)
}

View File

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

View File

@ -1,25 +0,0 @@
// 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
tvTypes = listOf(
"TvSeries",
"Movie",
)
iconUrl = "https://www.google.com/s2/favicons?domain=melomovie.com&sz=24"
}

View File

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

View File

@ -1,195 +0,0 @@
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.addImdbUrl
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.getQualityFromName
import org.jsoup.Jsoup
import org.jsoup.nodes.Element
class MeloMovieProvider : MainAPI() {
override var name = "MeloMovie"
override var mainUrl = "https://melomovie.com"
override val instantLinkLoading = true
override val hasQuickSearch = true
override val hasChromecastSupport = false // MKV FILES CANT BE PLAYED ON A CHROMECAST
data class MeloMovieSearchResult(
@JsonProperty("id") val id: Int,
@JsonProperty("imdb_code") val imdbId: String,
@JsonProperty("title") val title: String,
@JsonProperty("type") val type: Int, // 1 = MOVIE, 2 = TV-SERIES
@JsonProperty("year") val year: Int?, // 1 = MOVIE, 2 = TV-SERIES
//"mppa" for tags
)
data class MeloMovieLink(
@JsonProperty("name") val name: String,
@JsonProperty("link") val link: String
)
override suspend fun quickSearch(query: String): List<SearchResponse> {
return search(query)
}
override suspend fun search(query: String): List<SearchResponse> {
val url = "$mainUrl/movie/search/?name=$query"
val returnValue: ArrayList<SearchResponse> = ArrayList()
val response = app.get(url).text
val mapped = response.let { mapper.readValue<List<MeloMovieSearchResult>>(it) }
if (mapped.isEmpty()) return returnValue
for (i in mapped) {
val currentUrl = "$mainUrl/movie/${i.id}"
val currentPoster = "$mainUrl/assets/images/poster/${i.imdbId}.jpg"
if (i.type == 2) { // TV-SERIES
returnValue.add(
TvSeriesSearchResponse(
i.title,
currentUrl,
this.name,
TvType.TvSeries,
currentPoster,
i.year,
null
)
)
} else if (i.type == 1) { // MOVIE
returnValue.add(
MovieSearchResponse(
i.title,
currentUrl,
this.name,
TvType.Movie,
currentUrl,
i.year
)
)
}
}
return returnValue
}
// http not https, the links are not https!
private fun fixUrl(url: String): String {
if (url.isEmpty()) return ""
if (url.startsWith("//")) {
return "http:$url"
}
if (!url.startsWith("http")) {
return "http://$url"
}
return url
}
private fun serializeData(element: Element): List<MeloMovieProvider.MeloMovieLink> {
val eps = element.select("> tbody > tr")
val parsed = eps.mapNotNull {
try {
val tds = it.select("> td")
val name = tds[if (tds.size == 5) 1 else 0].text()
val url = fixUrl(tds.last()!!.selectFirst("> a")!!.attr("data-lnk").replace(" ", "%20"))
MeloMovieLink(name, url)
} catch (e: Exception) {
MeloMovieLink("", "")
}
}.filter { it.link != "" && it.name != "" }
return parsed
}
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
val links = parseJson<List<MeloMovieLink>>(data)
for (link in links) {
callback.invoke(
ExtractorLink(
this.name,
link.name,
link.link,
"",
getQualityFromName(link.name),
false
)
)
}
return true
}
override suspend fun load(url: String): LoadResponse? {
val response = app.get(url).text
//backdrop = imgurl
fun findUsingRegex(src: String): String? {
return src.toRegex().find(response)?.groups?.get(1)?.value ?: return null
}
val imdbUrl = findUsingRegex("var imdb = \"(.*?)\"")
val document = Jsoup.parse(response)
val poster = document.selectFirst("img.img-fluid")!!.attr("src")
val type = findUsingRegex("var posttype = ([0-9]*)")?.toInt() ?: return null
val titleInfo = document.selectFirst("div.movie_detail_title > div > div > h1")
val title = titleInfo!!.ownText()
val year =
titleInfo.selectFirst("> a")?.text()?.replace("(", "")?.replace(")", "")?.toIntOrNull()
val plot = document.selectFirst("div.col-lg-12 > p")!!.text()
if (type == 1) { // MOVIE
val serialize = document.selectFirst("table.accordion__list")
?: throw ErrorLoadingException("No links found")
return newMovieLoadResponse(
title,
url,
TvType.Movie,
serializeData(serialize)
) {
this.posterUrl = poster
this.year = year
this.plot = plot
addImdbUrl(imdbUrl)
}
} else if (type == 2) {
val episodes = ArrayList<Episode>()
val seasons = document.select("div.accordion__card")
?: throw ErrorLoadingException("No episodes found")
for (s in seasons) {
val season =
s.selectFirst("> div.card-header > button > span")!!.text()
.replace("Season: ", "").toIntOrNull()
val localEpisodes = s.select("> div.collapse > div > div > div.accordion__card")
for (e in localEpisodes) {
val episode =
e.selectFirst("> div.card-header > button > span")!!.text()
.replace("Episode: ", "").toIntOrNull()
val links =
e.selectFirst("> div.collapse > div > table.accordion__list") ?: continue
val data = serializeData(links)
episodes.add(newEpisode(data) {
this.season = season
this.episode = episode
})
}
}
episodes.reverse()
return newTvSeriesLoadResponse(
title,
url,
TvType.TvSeries,
episodes
) {
this.posterUrl = poster
this.year = year
this.plot = plot
addImdbUrl(imdbUrl)
}
}
return null
}
}

View File

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

View File

@ -1,25 +0,0 @@
// 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
tvTypes = listOf(
"AnimeMovie",
"TvSeries",
"Movie",
)
}

View File

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

View File

@ -1,286 +0,0 @@
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.Qualities
class NginxProvider : MainAPI() {
override var name = "Nginx"
override val hasQuickSearch = false
override val hasMainPage = true
override val supportedTypes = setOf(TvType.AnimeMovie, TvType.TvSeries, TvType.Movie)
companion object {
var loginCredentials: String? = null
var overrideUrl: String? = null
const val ERROR_STRING = "No nginx url specified in the settings"
}
private fun getAuthHeader(): Map<String, String> {
val url = overrideUrl ?: throw ErrorLoadingException(ERROR_STRING)
mainUrl = url
println("OVERRIDING URL TO $overrideUrl")
if (mainUrl == "NONE" || mainUrl.isBlank()) {
throw ErrorLoadingException(ERROR_STRING)
}
val localCredentials = loginCredentials
if (localCredentials == null || localCredentials.trim() == ":") {
return mapOf("Authorization" to "Basic ") // no Authorization headers
}
val basicAuthToken =
base64Encode(localCredentials.toByteArray()) // will this be loaded when not using the provider ??? can increase load
return mapOf("Authorization" to "Basic $basicAuthToken")
}
override suspend fun load(url: String): LoadResponse {
val authHeader =
getAuthHeader() // call again because it isn't reloaded if in main class and storedCredentials loads after
// url can be tvshow.nfo for series or mediaRootUrl for movies
val mainRootDocument = app.get(url, authHeader).document
val nfoUrl = url + mainRootDocument.getElementsByAttributeValueContaining("href", ".nfo")
.attr("href") // metadata url file
val metadataDocument = app.get(nfoUrl, authHeader).document // get the metadata nfo file
val isMovie = !nfoUrl.contains("tvshow.nfo")
val title = metadataDocument.selectFirst("title")!!.text()
val description = metadataDocument.selectFirst("plot")!!.text()
if (isMovie) {
val poster = metadataDocument.selectFirst("thumb")!!.text()
val trailer = metadataDocument.select("trailer").mapNotNull {
it?.text()?.replace(
"plugin://plugin.video.youtube/play/?video_id=",
"https://www.youtube.com/watch?v="
)
}
val partialUrl =
mainRootDocument.getElementsByAttributeValueContaining("href", ".nfo").attr("href")
.replace(".nfo", ".")
val date = metadataDocument.selectFirst("year")?.text()?.toIntOrNull()
val ratingAverage = metadataDocument.selectFirst("value")?.text()?.toIntOrNull()
val tagsList = metadataDocument.select("genre")
.mapNotNull { // all the tags like action, thriller ...
it?.text()
}
val dataList =
mainRootDocument.getElementsByAttributeValueContaining( // list of all urls of the webpage
"href",
partialUrl
)
val data = url + dataList.firstNotNullOf { item ->
item.takeIf {
(!it.attr("href").contains(".nfo") && !it.attr("href").contains(".jpg"))
}
}.attr("href").toString() // exclude poster and nfo (metadata) file
return newMovieLoadResponse(
title,
url,
TvType.Movie,
data
) {
this.year = date
this.plot = description
this.rating = ratingAverage
this.tags = tagsList
addTrailer(trailer)
addPoster(poster, authHeader)
}
} else // a tv serie
{
val list = ArrayList<Pair<Int, String>>()
val mediaRootUrl = url.replace("tvshow.nfo", "")
val posterUrl = mediaRootUrl + "poster.jpg"
val mediaRootDocument = app.get(mediaRootUrl, authHeader).document
val seasons =
mediaRootDocument.getElementsByAttributeValueContaining("href", "Season%20")
val tagsList = metadataDocument.select("genre")
.mapNotNull { // all the tags like action, thriller ...; unused variable
it?.text()
}
//val actorsList = document.select("actor")
// ?.mapNotNull { // all the tags like action, thriller ...; unused variable
// it?.text()
// }
seasons.forEach { element ->
val season =
element.attr("href").replace("Season%20", "").replace("/", "").toIntOrNull()
val href = mediaRootUrl + element.attr("href")
if (season != null && season > 0 && href.isNotBlank()) {
list.add(Pair(season, href))
}
}
if (list.isEmpty()) throw ErrorLoadingException("No Seasons Found")
val episodeList = ArrayList<Episode>()
list.apmap { (seasonInt, seasonString) ->
val seasonDocument = app.get(seasonString, authHeader).document
val episodes = seasonDocument.getElementsByAttributeValueContaining(
"href",
".nfo"
) // get metadata
episodes.forEach { episode ->
val nfoDocument = app.get(
seasonString + episode.attr("href"),
authHeader
).document // get episode metadata file
val epNum = nfoDocument.selectFirst("episode")?.text()?.toIntOrNull()
val poster =
seasonString + episode.attr("href").replace(".nfo", "-thumb.jpg")
val name = nfoDocument.selectFirst("title")!!.text()
// val seasonInt = nfoDocument.selectFirst("season").text().toIntOrNull()
val date = nfoDocument.selectFirst("aired")?.text()
val plot = nfoDocument.selectFirst("plot")?.text()
val dataList = seasonDocument.getElementsByAttributeValueContaining(
"href",
episode.attr("href").replace(".nfo", "")
)
val data = seasonString + dataList.firstNotNullOf { item ->
item.takeIf {
(!it.attr("href").contains(".nfo") && !it.attr("href").contains(".jpg"))
}
}.attr("href").toString() // exclude poster and nfo (metadata) file
episodeList.add(
newEpisode(data) {
this.name = name
this.season = seasonInt
this.episode = epNum
this.posterUrl = poster // will require headers too
this.description = plot
addDate(date)
}
)
}
}
return newTvSeriesLoadResponse(title, url, TvType.TvSeries, episodeList) {
this.name = title
this.url = url
this.episodes = episodeList
this.plot = description
this.tags = tagsList
addPoster(posterUrl, authHeader)
}
}
}
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
// loadExtractor(data, null) { callback(it.copy(headers=authHeader)) }
val authHeader =
getAuthHeader() // call again because it isn't reloaded if in main class and storedCredentials loads after
callback.invoke(
ExtractorLink(
name,
name,
data,
data, // referer not needed
Qualities.Unknown.value,
false,
authHeader,
)
)
return true
}
override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse {
val authHeader =
getAuthHeader() // call again because it isn't reloaded if in main class and storedCredentials loads after
val document = app.get(mainUrl, authHeader).document
val categories = document.select("a")
val returnList = categories.mapNotNull {
val categoryTitle = it.text() // get the category title like Movies or Series
if (categoryTitle != "../" && categoryTitle != "Music/") { // exclude parent dir and Music dir
val href = it?.attr("href")
val categoryPath = fixUrlNull(href?.trim())
?: return@mapNotNull null // get the url of the category; like http://192.168.1.10/media/Movies/
val categoryDocument = app.get(
categoryPath,
authHeader
).document // queries the page http://192.168.1.10/media/Movies/
val contentLinks = categoryDocument.select("a")
val currentList = contentLinks.mapNotNull { head ->
if (head.attr("href") != "../") {
try {
val mediaRootUrl =
categoryPath + head.attr("href")// like http://192.168.1.10/media/Series/Chernobyl/
val mediaDocument = app.get(mediaRootUrl, authHeader).document
val nfoFilename = mediaDocument.getElementsByAttributeValueContaining(
"href",
".nfo"
)[0].attr("href")
val isMovieType = nfoFilename != "tvshow.nfo"
val nfoPath =
mediaRootUrl + nfoFilename // must exist or will raise errors, only the first one is taken
val nfoContent =
app.get(nfoPath, authHeader).document // all the metadata
if (isMovieType) {
val movieName = nfoContent.select("title").text()
val posterUrl = mediaRootUrl + "poster.jpg"
return@mapNotNull newMovieSearchResponse(
movieName,
mediaRootUrl,
TvType.Movie,
) {
addPoster(posterUrl, authHeader)
}
} else { // tv serie
val serieName = nfoContent.select("title").text()
val posterUrl = mediaRootUrl + "poster.jpg"
newTvSeriesSearchResponse(
serieName,
nfoPath,
TvType.TvSeries,
) {
addPoster(posterUrl, authHeader)
}
}
} catch (e: Exception) { // can cause issues invisible errors
null
//logError(e) // not working because it changes the return type of currentList to Any
}
} else null
}
if (currentList.isNotEmpty() && categoryTitle != "../") { // exclude upper dir
HomePageList(categoryTitle, currentList)
} else null
} else null // the path is ../ which is parent directory
}
// if (returnList.isEmpty()) return null // maybe doing nothing idk
return HomePageResponse(returnList)
}
}

View File

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

View File

@ -1,25 +0,0 @@
// 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
tvTypes = listOf(
"Anime",
"OVA",
)
iconUrl = "https://www.google.com/s2/favicons?domain=9anime.id&sz=24"
}

View File

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

View File

@ -1,357 +0,0 @@
package com.lagradost
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.Qualities
import com.lagradost.cloudstream3.utils.loadExtractor
import org.jsoup.Jsoup
class NineAnimeProvider : MainAPI() {
override var mainUrl = "https://9anime.id"
override var name = "9Anime"
override val hasMainPage = true
override val hasChromecastSupport = true
override val hasDownloadSupport = true
override val supportedTypes = setOf(TvType.Anime)
override val hasQuickSearch = true
// taken from https://github.com/saikou-app/saikou/blob/b35364c8c2a00364178a472fccf1ab72f09815b4/app/src/main/java/ani/saikou/parsers/anime/NineAnime.kt
// GNU General Public License v3.0 https://github.com/saikou-app/saikou/blob/main/LICENSE.md
companion object {
private const val nineAnimeKey =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
private const val cipherKey = "kMXzgyNzT3k5dYab"
fun encodeVrf(text: String, mainKey: String): String {
return encode(
encrypt(
cipher(mainKey, encode(text)),
nineAnimeKey
)//.replace("""=+$""".toRegex(), "")
)
}
fun decodeVrf(text: String, mainKey: String): String {
return decode(cipher(mainKey, decrypt(text, nineAnimeKey)))
}
fun encrypt(input: String, key: String): String {
if (input.any { it.code > 255 }) throw Exception("illegal characters!")
var output = ""
for (i in input.indices step 3) {
val a = intArrayOf(-1, -1, -1, -1)
a[0] = input[i].code shr 2
a[1] = (3 and input[i].code) shl 4
if (input.length > i + 1) {
a[1] = a[1] or (input[i + 1].code shr 4)
a[2] = (15 and input[i + 1].code) shl 2
}
if (input.length > i + 2) {
a[2] = a[2] or (input[i + 2].code shr 6)
a[3] = 63 and input[i + 2].code
}
for (n in a) {
if (n == -1) output += "="
else {
if (n in 0..63) output += key[n]
}
}
}
return output
}
fun cipher(key: String, text: String): String {
val arr = IntArray(256) { it }
var u = 0
var r: Int
arr.indices.forEach {
u = (u + arr[it] + key[it % key.length].code) % 256
r = arr[it]
arr[it] = arr[u]
arr[u] = r
}
u = 0
var c = 0
return text.indices.map { j ->
c = (c + 1) % 256
u = (u + arr[c]) % 256
r = arr[c]
arr[c] = arr[u]
arr[u] = r
(text[j].code xor arr[(arr[c] + arr[u]) % 256]).toChar()
}.joinToString("")
}
@Suppress("SameParameterValue")
private fun decrypt(input: String, key: String): String {
val t = if (input.replace("""[\t\n\f\r]""".toRegex(), "").length % 4 == 0) {
input.replace("""==?$""".toRegex(), "")
} else input
if (t.length % 4 == 1 || t.contains("""[^+/0-9A-Za-z]""".toRegex())) throw Exception("bad input")
var i: Int
var r = ""
var e = 0
var u = 0
for (o in t.indices) {
e = e shl 6
i = key.indexOf(t[o])
e = e or i
u += 6
if (24 == u) {
r += ((16711680 and e) shr 16).toChar()
r += ((65280 and e) shr 8).toChar()
r += (255 and e).toChar()
e = 0
u = 0
}
}
return if (12 == u) {
e = e shr 4
r + e.toChar()
} else {
if (18 == u) {
e = e shr 2
r += ((65280 and e) shr 8).toChar()
r += (255 and e).toChar()
}
r
}
}
fun encode(input: String): String =
java.net.URLEncoder.encode(input, "utf-8").replace("+", "%20")
private fun decode(input: String): String = java.net.URLDecoder.decode(input, "utf-8")
}
override val mainPage = mainPageOf(
"$mainUrl/ajax/home/widget/trending?page=" to "Trending",
"$mainUrl/ajax/home/widget/updated-all?page=" to "All",
"$mainUrl/ajax/home/widget/updated-sub?page=" to "Recently Updated (SUB)",
"$mainUrl/ajax/home/widget/updated-dub?page=" to "Recently Updated (DUB)",
"$mainUrl/ajax/home/widget/updated-china?page=" to "Recently Updated (Chinese)",
"$mainUrl/ajax/home/widget/random?page=" to "Random",
)
override suspend fun getMainPage(
page: Int,
request: MainPageRequest
): HomePageResponse {
val url = request.data + page
val home = Jsoup.parse(
app.get(
url
).parsed<Response>().html
).select("div.item").mapNotNull { element ->
val title = element.selectFirst(".info > .name") ?: return@mapNotNull null
val link = title.attr("href")
val poster = element.selectFirst(".poster > a > img")?.attr("src")
val meta = element.selectFirst(".poster > a > .meta > .inner > .left")
val subbedEpisodes = meta?.selectFirst(".sub")?.text()?.toIntOrNull()
val dubbedEpisodes = meta?.selectFirst(".dub")?.text()?.toIntOrNull()
newAnimeSearchResponse(title.text() ?: return@mapNotNull null, link) {
this.posterUrl = poster
addDubStatus(
dubbedEpisodes != null,
subbedEpisodes != null,
dubbedEpisodes,
subbedEpisodes
)
}
}
return newHomePageResponse(request.name, home)
}
data class Response(
@JsonProperty("result") val html: String
)
data class QuickSearchResponse(
//@JsonProperty("status") val status: Int? = null,
@JsonProperty("result") val result: QuickSearchResult? = null,
//@JsonProperty("message") val message: String? = null,
//@JsonProperty("messages") val messages: ArrayList<String> = arrayListOf()
)
data class QuickSearchResult(
@JsonProperty("html") val html: String? = null,
//@JsonProperty("linkMore") val linkMore: String? = null
)
override suspend fun quickSearch(query: String): List<SearchResponse>? {
val vrf = encodeVrf(query, cipherKey)
val url =
"$mainUrl/ajax/anime/search?keyword=$query&vrf=$vrf"
val response = app.get(url).parsedSafe<QuickSearchResponse>()
val document = Jsoup.parse(response?.result?.html ?: return null)
return document.select(".items > a").mapNotNull { element ->
val link = fixUrl(element?.attr("href") ?: return@mapNotNull null)
val title = element.selectFirst(".info > .name")?.text() ?: return@mapNotNull null
newAnimeSearchResponse(title, link) {
posterUrl = element.selectFirst(".poster > span > img")?.attr("src")
}
}
}
override suspend fun search(query: String): List<SearchResponse> {
val vrf = encodeVrf(query, cipherKey)
//?language%5B%5D=${if (selectDub) "dubbed" else "subbed"}&
val url =
"$mainUrl/filter?keyword=${encode(query)}&vrf=${vrf}&page=1"
return app.get(url).document.select("#list-items div.ani.poster.tip > a").mapNotNull {
val link = fixUrl(it.attr("href") ?: return@mapNotNull null)
val img = it.select("img")
val title = img.attr("alt")
newAnimeSearchResponse(title, link) {
posterUrl = img.attr("src")
}
}
}
override suspend fun load(url: String): LoadResponse {
val validUrl = url.replace("https://9anime.to", mainUrl)
val doc = app.get(validUrl).document
val meta = doc.selectFirst("#w-info") ?: throw ErrorLoadingException("Could not find info")
val ratingElement = meta.selectFirst(".brating > #w-rating")
val id = ratingElement?.attr("data-id") ?: throw ErrorLoadingException("Could not find id")
val binfo =
meta.selectFirst(".binfo") ?: throw ErrorLoadingException("Could not find binfo")
val info = binfo.selectFirst(".info") ?: throw ErrorLoadingException("Could not find info")
val title = (info.selectFirst(".title") ?: info.selectFirst(".d-title"))?.text()
?: throw ErrorLoadingException("Could not find title")
val vrf = encodeVrf(id, cipherKey)
val episodeListUrl = "$mainUrl/ajax/episode/list/$id?vrf=$vrf"
val body =
app.get(episodeListUrl).parsedSafe<Response>()?.html
?: throw ErrorLoadingException("Could not parse json with cipherKey=$cipherKey id=$id url=\n$episodeListUrl")
val subEpisodes = ArrayList<Episode>()
val dubEpisodes = ArrayList<Episode>()
//TODO RECOMMENDATIONS
Jsoup.parse(body).body().select(".episodes > ul > li > a").mapNotNull { element ->
val ids = element.attr("data-ids").split(",", limit = 2)
val epNum = element.attr("data-num")
.toIntOrNull() // might fuck up on 7.5 ect might use data-slug instead
val epTitle = element.selectFirst("span.d-title")?.text()
//val filler = element.hasClass("filler")
ids.getOrNull(1)?.let { dub ->
dubEpisodes.add(
Episode(
"$mainUrl/ajax/server/list/$dub?vrf=${encodeVrf(dub, cipherKey)}",
epTitle,
episode = epNum
)
)
}
ids.getOrNull(0)?.let { sub ->
subEpisodes.add(
Episode(
"$mainUrl/ajax/server/list/$sub?vrf=${encodeVrf(sub, cipherKey)}",
epTitle,
episode = epNum
)
)
}
}
return newAnimeLoadResponse(title, url, TvType.Anime) {
addEpisodes(DubStatus.Dubbed, dubEpisodes)
addEpisodes(DubStatus.Subbed, subEpisodes)
plot = info.selectFirst(".synopsis > .shorting > .content")?.text()
posterUrl = binfo.selectFirst(".poster > span > img")?.attr("src")
rating = ratingElement.attr("data-score").toFloat().times(1000f).toInt()
info.select(".bmeta > .meta > div").forEach { element ->
when (element.ownText()) {
"Genre: " -> {
tags = element.select("span > a").mapNotNull { it?.text() }
}
"Duration: " -> {
duration = getDurationFromString(element.selectFirst("span")?.text())
}
"Type: " -> {
type = when (element.selectFirst("span > a")?.text()) {
"ONA" -> TvType.OVA
else -> {
type
}
}
}
"Status: " -> {
showStatus = when (element.selectFirst("span")?.text()) {
"Releasing" -> ShowStatus.Ongoing
"Completed" -> ShowStatus.Completed
else -> {
showStatus
}
}
}
else -> {}
}
}
}
}
data class Result(
@JsonProperty("url")
val url: String? = null
)
data class Links(
@JsonProperty("result")
val result: Result? = null
)
//TODO 9anime outro into {"status":200,"result":{"url":"","skip_data":{"intro_begin":67,"intro_end":154,"outro_begin":1337,"outro_end":1415,"count":3}},"message":"","messages":[]}
private suspend fun getEpisodeLinks(id: String): Links? {
return app.get("$mainUrl/ajax/server/$id?vrf=${encodeVrf(id, cipherKey)}").parsedSafe()
}
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
val body = app.get(data).parsed<Response>().html
val document = Jsoup.parse(body)
document.select("li").apmap {
try {
val name = it.text()
val encodedStreamUrl =
getEpisodeLinks(it.attr("data-link-id"))?.result?.url ?: return@apmap
val url = decodeVrf(encodedStreamUrl, cipherKey)
if (!loadExtractor(url, mainUrl, subtitleCallback, callback)) {
callback(
ExtractorLink(
this.name,
name,
url,
mainUrl,
Qualities.Unknown.value,
url.contains(".m3u8")
)
)
}
} catch (e: Exception) {
logError(e)
}
}
return true
}
}

View File

@ -1,27 +0,0 @@
package com.lagradost
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
import com.lagradost.cloudstream3.plugins.Plugin
import android.content.Context
@CloudstreamPlugin
class NineAnimeProviderPlugin : Plugin() {
override fun load(context: Context) {
// All providers should be added in this manner. Please don't edit the providers list directly.
registerMainAPI(NineAnimeProvider())
registerMainAPI(WcoProvider())
registerExtractorAPI(Mcloud())
registerExtractorAPI(Vidstreamz())
registerExtractorAPI(Vizcloud())
registerExtractorAPI(Vizcloud2())
registerExtractorAPI(VizcloudOnline())
registerExtractorAPI(VizcloudXyz())
registerExtractorAPI(VizcloudLive())
registerExtractorAPI(VizcloudInfo())
registerExtractorAPI(MwvnVizcloudInfo())
registerExtractorAPI(VizcloudDigital())
registerExtractorAPI(VizcloudCloud())
registerExtractorAPI(VizcloudSite())
registerExtractorAPI(WcoStream())
}
}

View File

@ -1,238 +0,0 @@
package com.lagradost
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.utils.ExtractorLink
import org.json.JSONObject
import org.jsoup.Jsoup
import org.jsoup.nodes.Document
import java.util.*
class WcoProvider : 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://wcostream.cc"
override var name = "WCO Stream"
override val hasQuickSearch = true
override val hasMainPage = true
override val supportedTypes = setOf(
TvType.AnimeMovie,
TvType.Anime,
TvType.OVA
)
override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse {
val urls = listOf(
Pair("$mainUrl/ajax/list/recently_updated?type=tv", "Recently Updated Anime"),
Pair("$mainUrl/ajax/list/recently_updated?type=movie", "Recently Updated Movies"),
Pair("$mainUrl/ajax/list/recently_added?type=tv", "Recently Added Anime"),
Pair("$mainUrl/ajax/list/recently_added?type=movie", "Recently Added Movies"),
)
val items = ArrayList<HomePageList>()
for (i in urls) {
try {
val response = JSONObject(
app.get(
i.first,
).text
).getString("html") // I won't make a dataclass for this shit
val document = Jsoup.parse(response)
val results = document.select("div.flw-item").map {
val filmPoster = it.selectFirst("> div.film-poster")
val filmDetail = it.selectFirst("> div.film-detail")
val nameHeader = filmDetail!!.selectFirst("> h3.film-name > a")
val title = nameHeader!!.text().replace(" (Dub)", "")
val href =
nameHeader.attr("href").replace("/watch/", "/anime/")
.replace(Regex("-episode-.*"), "/")
val isDub =
filmPoster!!.selectFirst("> div.film-poster-quality")?.text()
?.contains("DUB")
?: false
val poster = filmPoster.selectFirst("> img")!!.attr("data-src")
val set: EnumSet<DubStatus> =
EnumSet.of(if (isDub) DubStatus.Dubbed else DubStatus.Subbed)
AnimeSearchResponse(title, href, this.name, TvType.Anime, poster, null, set)
}
items.add(HomePageList(i.second, results))
} catch (e: Exception) {
e.printStackTrace()
}
}
if (items.size <= 0) throw ErrorLoadingException()
return HomePageResponse(items)
}
private fun fixAnimeLink(url: String): String {
val regex = "watch/([a-zA-Z\\-0-9]*)-episode".toRegex()
val (aniId) = regex.find(url)!!.destructured
return "$mainUrl/anime/$aniId"
}
private fun parseSearchPage(soup: Document): List<SearchResponse> {
val items = soup.select(".film_list-wrap > .flw-item")
if (items.isEmpty()) return ArrayList()
return items.map { i ->
val href = fixAnimeLink(i.selectFirst("a")!!.attr("href"))
val img = fixUrl(i.selectFirst("img")!!.attr("data-src"))
val title = i.selectFirst("img")!!.attr("title")
val isDub = !i.select(".pick.film-poster-quality").isEmpty()
val year =
i.selectFirst(".film-detail.film-detail-fix > div > span:nth-child(1)")!!.text()
.toIntOrNull()
val type =
i.selectFirst(".film-detail.film-detail-fix > div > span:nth-child(3)")!!.text()
if (getType(type) == TvType.AnimeMovie) {
MovieSearchResponse(
title, href, this.name, TvType.AnimeMovie, img, year
)
} else {
AnimeSearchResponse(
title,
href,
this.name,
TvType.Anime,
img,
year,
EnumSet.of(if (isDub) DubStatus.Dubbed else DubStatus.Subbed),
)
}
}
}
override suspend fun search(query: String): List<SearchResponse> {
val url = "$mainUrl/search"
val response =
app.get(url, params = mapOf("keyword" to query))
var document = Jsoup.parse(response.text)
val returnValue = parseSearchPage(document).toMutableList()
while (!document.select(".pagination").isEmpty()) {
val link = document.select("a.page-link[rel=\"next\"]")
if (!link.isEmpty() && returnValue.size < 40) {
val extraResponse = app.get(fixUrl(link[0].attr("href"))).text
document = Jsoup.parse(extraResponse)
returnValue.addAll(parseSearchPage(document))
} else {
break
}
}
return returnValue.distinctBy { it.url }
}
override suspend fun quickSearch(query: String): List<SearchResponse> {
val response = JSONObject(
app.post(
"https://wcostream.cc/ajax/search",
data = mapOf("keyword" to query)
).text
).getString("html") // I won't make a dataclass for this shit
val document = Jsoup.parse(response)
return document.select("a.nav-item").mapNotNull {
val title = it.selectFirst("img")?.attr("title") ?: return@mapNotNull null
val img = it?.selectFirst("img")?.attr("src") ?: return@mapNotNull null
val href = it?.attr("href") ?: return@mapNotNull null
val isDub = title.contains("(Dub)")
val filmInfo = it.selectFirst(".film-infor")
val year = filmInfo?.select("span")?.get(0)?.text()?.toIntOrNull()
val type = filmInfo?.select("span")?.get(1)?.text().toString()
if (getType(type) == TvType.AnimeMovie) {
MovieSearchResponse(
title, href, this.name, TvType.AnimeMovie, img, year
)
} else {
AnimeSearchResponse(
title,
href,
this.name,
TvType.Anime,
img,
year,
EnumSet.of(if (isDub) DubStatus.Dubbed else DubStatus.Subbed),
)
}
}
}
override suspend fun load(url: String): LoadResponse {
val response = app.get(url, timeout = 120).text
val document = Jsoup.parse(response)
val japaneseTitle =
document.selectFirst("div.elements div.row > div:nth-child(1) > div.row-line:nth-child(1)")
?.text()?.trim()?.replace("Other names:", "")?.trim()
val canonicalTitle = document.selectFirst("meta[name=\"title\"]")
?.attr("content")?.split("| W")?.get(0).toString()
val isDubbed = canonicalTitle.contains("Dub")
val episodeNodes = document.select(".tab-content .nav-item > a")
val episodes = ArrayList(episodeNodes?.map {
Episode(it.attr("href"))
} ?: ArrayList())
val statusElem =
document.selectFirst("div.elements div.row > div:nth-child(1) > div.row-line:nth-child(2)")
val status = when (statusElem?.text()?.replace("Status:", "")?.trim()) {
"Ongoing" -> ShowStatus.Ongoing
"Completed" -> ShowStatus.Completed
else -> null
}
val yearText =
document.selectFirst("div.elements div.row > div:nth-child(2) > div.row-line:nth-child(4)")
?.text()
val year = yearText?.replace("Date release:", "")?.trim()?.split("-")?.get(0)?.toIntOrNull()
val poster = document.selectFirst(".film-poster-img")?.attr("src")
val type = document.selectFirst("span.item.mr-1 > a")?.text()?.trim()
val synopsis = document.selectFirst(".description > p")?.text()?.trim()
val genre =
document.select("div.elements div.row > div:nth-child(1) > div.row-line:nth-child(5) > a")
.map { it?.text()?.trim().toString() }
return newAnimeLoadResponse(canonicalTitle, url, getType(type ?: "")) {
japName = japaneseTitle
engName = canonicalTitle
posterUrl = poster
this.year = year
addEpisodes(if (isDubbed) DubStatus.Dubbed else DubStatus.Subbed, episodes)
showStatus = status
plot = synopsis
tags = genre
}
}
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
val response = app.get(data).text
val servers = Jsoup.parse(response).select("#servers-list > ul > li").map {
mapOf(
"link" to it?.selectFirst("a")?.attr("data-embed"),
"title" to it?.selectFirst("span")?.text()?.trim()
)
}
for (server in servers) {
WcoStream().getSafeUrl(server["link"].toString(), null, subtitleCallback, callback)
Mcloud().getSafeUrl(server["link"].toString(), null, subtitleCallback, callback)
}
return true
}
}

View File

@ -1,133 +0,0 @@
package com.lagradost
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.NineAnimeProvider.Companion.cipher
import com.lagradost.NineAnimeProvider.Companion.encrypt
import com.lagradost.cloudstream3.ErrorLoadingException
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.Qualities
class Vidstreamz : WcoStream() {
override var mainUrl = "https://vidstreamz.online"
}
open class Mcloud : WcoStream() {
override var name = "Mcloud"
override var mainUrl = "https://mcloud.to"
override val requiresReferer = true
}
class Vizcloud : WcoStream() {
override var mainUrl = "https://vizcloud2.ru"
}
class Vizcloud2 : WcoStream() {
override var mainUrl = "https://vizcloud2.online"
}
class VizcloudOnline : WcoStream() {
override var mainUrl = "https://vizcloud.online"
}
class VizcloudXyz : WcoStream() {
override var mainUrl = "https://vizcloud.xyz"
}
class VizcloudLive : WcoStream() {
override var mainUrl = "https://vizcloud.live"
}
class VizcloudInfo : WcoStream() {
override var mainUrl = "https://vizcloud.info"
}
class MwvnVizcloudInfo : WcoStream() {
override var mainUrl = "https://mwvn.vizcloud.info"
}
class VizcloudDigital : WcoStream() {
override var mainUrl = "https://vizcloud.digital"
}
class VizcloudCloud : WcoStream() {
override var mainUrl = "https://vizcloud.cloud"
}
class VizcloudSite : WcoStream() {
override var mainUrl = "https://vizcloud.site"
}
open class WcoStream : ExtractorApi() {
override var name = "VidStream" // Cause works for animekisa and wco
override var mainUrl = "https://vidstream.pro"
override val requiresReferer = false
private val regex = Regex("(.+?/)e(?:mbed)?/([a-zA-Z0-9]+)")
companion object {
// taken from https://github.com/saikou-app/saikou/blob/b35364c8c2a00364178a472fccf1ab72f09815b4/app/src/main/java/ani/saikou/parsers/anime/extractors/VizCloud.kt
// GNU General Public License v3.0 https://github.com/saikou-app/saikou/blob/main/LICENSE.md
private var lastChecked = 0L
private const val jsonLink =
"https://raw.githubusercontent.com/chenkaslowankiya/BruhFlow/main/keys.json"
private var cipherKey: VizCloudKey? = null
suspend fun getKey(): VizCloudKey {
cipherKey =
if (cipherKey != null && (lastChecked - System.currentTimeMillis()) < 1000 * 60 * 30) cipherKey!!
else {
lastChecked = System.currentTimeMillis()
app.get(jsonLink).parsed()
}
return cipherKey!!
}
data class VizCloudKey(
@JsonProperty("cipherKey") val cipherKey: String,
@JsonProperty("mainKey") val mainKey: String,
@JsonProperty("encryptKey") val encryptKey: String,
@JsonProperty("dashTable") val dashTable: String
)
private const val baseTable =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+=/_"
private fun dashify(id: String, dashTable: String): String {
val table = dashTable.split(" ")
return id.mapIndexedNotNull { i, c ->
table.getOrNull((baseTable.indexOf(c) * 16) + (i % 16))
}.joinToString("-")
}
}
//private val key = "LCbu3iYC7ln24K7P" // key credits @Modder4869
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink> {
val group = regex.find(url)?.groupValues!!
val host = group[1]
val viz = getKey()
val id = encrypt(
cipher(
viz.cipherKey,
encrypt(group[2], viz.encryptKey).also { println(it) }
).also { println(it) },
viz.encryptKey
).also { println(it) }
val link =
"${host}mediainfo/${dashify(id, viz.dashTable)}?key=${viz.mainKey}" //
val response = app.get(link, referer = referer)
data class Sources(@JsonProperty("file") val file: String)
data class Media(@JsonProperty("sources") val sources: List<Sources>)
data class Data(@JsonProperty("media") val media: Media)
data class Response(@JsonProperty("data") val data: Data)
if (!response.text.startsWith("{")) throw ErrorLoadingException("Seems like 9Anime kiddies changed stuff again, Go touch some grass for bout an hour Or use a different Server")
return response.parsed<Response>().data.media.sources.map {
ExtractorLink(name, it.file,it.file,host,Qualities.Unknown.value,it.file.contains(".m3u8"))
}
}
}

View File

@ -1,25 +0,0 @@
// use an integer for version numbers
version = 3
cloudstream {
// All of these properties are optional, you can safely remove them
description = "Uses TMDB"
authors = listOf("Blatzar")
/**
* Status int as the following:
* 0: Down
* 1: Ok
* 2: Slow
* 3: Beta only
* */
status = 1 // will be 3 if unspecified
tvTypes = listOf(
"TvSeries",
"Movie",
)
iconUrl = "https://www.google.com/s2/favicons?domain=olgply.com&sz=24"
}

View File

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

View File

@ -1,115 +0,0 @@
package com.lagradost
import com.lagradost.cloudstream3.SubtitleFile
import com.lagradost.cloudstream3.TvType
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.metaproviders.TmdbLink
import com.lagradost.cloudstream3.metaproviders.TmdbProvider
import com.lagradost.cloudstream3.network.WebViewResolver
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.Qualities
import com.lagradost.nicehttp.requestCreator
import org.mozilla.javascript.Context
import org.mozilla.javascript.Scriptable
import org.mozilla.javascript.ScriptableObject
class OlgplyProvider : TmdbProvider() {
override var mainUrl = "https://olgply.com"
override val apiName = "Olgply"
override var name = "Olgply"
override val instantLinkLoading = true
override val useMetaLoadResponse = true
override val supportedTypes = setOf(TvType.TvSeries, TvType.Movie)
private suspend fun loadLinksWithWebView(
url: String,
// subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
) {
val foundVideo = WebViewResolver(
Regex("""\.m3u8|i7njdjvszykaieynzsogaysdgb0hm8u1mzubmush4maopa4wde\.com""")
).resolveUsingWebView(
requestCreator(
"GET", url, referer = "https://olgply.xyz/"
)
)
.first ?: return
callback.invoke(
ExtractorLink(
this.name,
"Movies4Discord",
foundVideo.url.toString(),
"",
Qualities.Unknown.value,
true
)
)
}
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
val mappedData = parseJson<TmdbLink>(data)
val tmdbId = mappedData.tmdbID ?: return false
val jsRegex = Regex("""eval\(.*\);""")
val apiUrl =
"https://olgply.xyz/${tmdbId}${mappedData.season?.let { "/$it" } ?: ""}${mappedData.episode?.let { "/$it" } ?: ""}"
// val html =
// app.get(apiUrl, referer = "https://olgply.xyz/").text
// val rhino = Context.enter()
// rhino.optimizationLevel = -1
// val scope: Scriptable = rhino.initSafeStandardObjects()
// val documentJs = """
// Plyr = function(){};
//
// hlsPrototype = {
// loadSource(url) {
// this.url = url;
// }
// };
//
// function Hls() {};
// Hls.isSupported = function(){return true};
//
// Hls.prototype = hlsPrototype;
// Hls.prototype.constructor = Hls;
//
// document = {
// "querySelector" : function() {}
// };
// """.trimIndent()
//
// val foundJs = jsRegex.find(html)?.groupValues?.getOrNull(0) ?: return false
// try {
// rhino.evaluateString(scope, documentJs + foundJs, "JavaScript", 1, null)
// } catch (e: Exception) {
// }
//
// val hls = scope.get("hls", scope) as? ScriptableObject
//
// if (hls != null) {
// callback.invoke(
// ExtractorLink(
// this.name,
// this.name,
// hls["url"].toString(),
// this.mainUrl + "/",
// Qualities.Unknown.value,
// headers = mapOf("range" to "bytes=0-"),
// isM3u8 = true
// )
// )
// } else {
// Disgraceful fallback, but the js for Movies4Discord refuses to work correctly :(
loadLinksWithWebView(apiUrl, callback)
// }
return true
}
}

View File

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

View File

@ -1,25 +0,0 @@
// use an integer for version numbers
version = 2
cloudstream {
// All of these properties are optional, you can safely remove them
description = "Also includes Dopebox, Solarmovie, Zoro and 2embed"
// authors = listOf("Cloudburst")
/**
* Status int as the following:
* 0: Down
* 1: Ok
* 2: Slow
* 3: Beta only
* */
status = 1 // will be 3 if unspecified
tvTypes = listOf(
"TvSeries",
"Movie",
)
iconUrl = "https://www.google.com/s2/favicons?domain=www.2embed.to&sz=24"
}

View File

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

View File

@ -1,6 +0,0 @@
package com.lagradost
class DopeboxProvider : SflixProvider() {
override var mainUrl = "https://dopebox.to"
override var name = "Dopebox"
}

View File

@ -1,6 +0,0 @@
package com.lagradost
class HDTodayProvider : SflixProvider() {
override var mainUrl = "https://hdtoday.cc"
override var name = "HDToday"
}

View File

@ -1,747 +0,0 @@
package com.lagradost
import android.util.Log
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.APIHolder.getCaptchaToken
import com.lagradost.cloudstream3.APIHolder.unixTimeMS
import com.lagradost.cloudstream3.LoadResponse.Companion.addActors
import com.lagradost.cloudstream3.LoadResponse.Companion.addDuration
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
//import com.lagradost.cloudstream3.animeproviders.ZoroProvider
import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.M3u8Helper
import com.lagradost.cloudstream3.utils.getQualityFromName
import com.lagradost.cloudstream3.utils.loadExtractor
import com.lagradost.nicehttp.NiceResponse
import kotlinx.coroutines.delay
import okhttp3.RequestBody.Companion.toRequestBody
import org.jsoup.Jsoup
import org.jsoup.nodes.Element
import java.net.URI
import java.util.*
import kotlin.system.measureTimeMillis
open class SflixProvider : MainAPI() {
override var mainUrl = "https://sflix.to"
override var name = "Sflix.to"
override val hasQuickSearch = false
override val hasMainPage = true
override val hasChromecastSupport = true
override val hasDownloadSupport = true
override val usesWebView = true
override val supportedTypes = setOf(
TvType.Movie,
TvType.TvSeries,
)
override val vpnStatus = VPNStatus.None
override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse {
val html = app.get("$mainUrl/home").text
val document = Jsoup.parse(html)
val all = ArrayList<HomePageList>()
val map = mapOf(
"Trending Movies" to "div#trending-movies",
"Trending TV Shows" to "div#trending-tv",
)
map.forEach {
all.add(HomePageList(
it.key,
document.select(it.value).select("div.flw-item").map { element ->
element.toSearchResult()
}
))
}
document.select("section.block_area.block_area_home.section-id-02").forEach {
val title = it.select("h2.cat-heading").text().trim()
val elements = it.select("div.flw-item").map { element ->
element.toSearchResult()
}
all.add(HomePageList(title, elements))
}
return HomePageResponse(all)
}
override suspend fun search(query: String): List<SearchResponse> {
val url = "$mainUrl/search/${query.replace(" ", "-")}"
val html = app.get(url).text
val document = Jsoup.parse(html)
return document.select("div.flw-item").map {
val title = it.select("h2.film-name").text()
val href = fixUrl(it.select("a").attr("href"))
val year = it.select("span.fdi-item").text().toIntOrNull()
val image = it.select("img").attr("data-src")
val isMovie = href.contains("/movie/")
val metaInfo = it.select("div.fd-infor > span.fdi-item")
// val rating = metaInfo[0].text()
val quality = getQualityFromString(metaInfo.getOrNull(1)?.text())
if (isMovie) {
MovieSearchResponse(
title,
href,
this.name,
TvType.Movie,
image,
year,
quality = quality
)
} else {
TvSeriesSearchResponse(
title,
href,
this.name,
TvType.TvSeries,
image,
year,
null,
quality = quality
)
}
}
}
override suspend fun load(url: String): LoadResponse {
val document = app.get(url).document
val details = document.select("div.detail_page-watch")
val img = details.select("img.film-poster-img")
val posterUrl = img.attr("src")
val title = img.attr("title") ?: throw ErrorLoadingException("No Title")
/*
val year = Regex("""[Rr]eleased:\s*(\d{4})""").find(
document.select("div.elements").text()
)?.groupValues?.get(1)?.toIntOrNull()
val duration = Regex("""[Dd]uration:\s*(\d*)""").find(
document.select("div.elements").text()
)?.groupValues?.get(1)?.trim()?.plus(" min")*/
var duration = document.selectFirst(".fs-item > .duration")?.text()?.trim()
var year: Int? = null
var tags: List<String>? = null
var cast: List<String>? = null
val youtubeTrailer = document.selectFirst("iframe#iframe-trailer")?.attr("data-src")
val rating = document.selectFirst(".fs-item > .imdb")?.text()?.trim()
?.removePrefix("IMDB:")?.toRatingInt()
document.select("div.elements > .row > div > .row-line").forEach { element ->
val type = element?.select(".type")?.text() ?: return@forEach
when {
type.contains("Released") -> {
year = Regex("\\d+").find(
element.ownText() ?: return@forEach
)?.groupValues?.firstOrNull()?.toIntOrNull()
}
type.contains("Genre") -> {
tags = element.select("a").mapNotNull { it.text() }
}
type.contains("Cast") -> {
cast = element.select("a").mapNotNull { it.text() }
}
type.contains("Duration") -> {
duration = duration ?: element.ownText().trim()
}
}
}
val plot = details.select("div.description").text().replace("Overview:", "").trim()
val isMovie = url.contains("/movie/")
// https://sflix.to/movie/free-never-say-never-again-hd-18317 -> 18317
val idRegex = Regex(""".*-(\d+)""")
val dataId = details.attr("data-id")
val id = if (dataId.isNullOrEmpty())
idRegex.find(url)?.groupValues?.get(1)
?: throw ErrorLoadingException("Unable to get id from '$url'")
else dataId
val recommendations =
document.select("div.film_list-wrap > div.flw-item").mapNotNull { element ->
val titleHeader =
element.select("div.film-detail > .film-name > a") ?: return@mapNotNull null
val recUrl = fixUrlNull(titleHeader.attr("href")) ?: return@mapNotNull null
val recTitle = titleHeader.text() ?: return@mapNotNull null
val poster = element.select("div.film-poster > img").attr("data-src")
MovieSearchResponse(
recTitle,
recUrl,
this.name,
if (recUrl.contains("/movie/")) TvType.Movie else TvType.TvSeries,
poster,
year = null
)
}
if (isMovie) {
// Movies
val episodesUrl = "$mainUrl/ajax/movie/episodes/$id"
val episodes = app.get(episodesUrl).text
// Supported streams, they're identical
val sourceIds = Jsoup.parse(episodes).select("a").mapNotNull { element ->
var sourceId = element.attr("data-id")
if (sourceId.isNullOrEmpty())
sourceId = element.attr("data-linkid")
if (element.select("span").text().trim().isValidServer()) {
if (sourceId.isNullOrEmpty()) {
fixUrlNull(element.attr("href"))
} else {
"$url.$sourceId".replace("/movie/", "/watch-movie/")
}
} else {
null
}
}
val comingSoon = sourceIds.isEmpty()
return newMovieLoadResponse(title, url, TvType.Movie, sourceIds) {
this.year = year
this.posterUrl = posterUrl
this.plot = plot
addDuration(duration)
addActors(cast)
this.tags = tags
this.recommendations = recommendations
this.comingSoon = comingSoon
addTrailer(youtubeTrailer)
this.rating = rating
}
} else {
val seasonsDocument = app.get("$mainUrl/ajax/v2/tv/seasons/$id").document
val episodes = arrayListOf<Episode>()
var seasonItems = seasonsDocument.select("div.dropdown-menu.dropdown-menu-model > a")
if (seasonItems.isNullOrEmpty())
seasonItems = seasonsDocument.select("div.dropdown-menu > a.dropdown-item")
seasonItems.apmapIndexed { season, element ->
val seasonId = element.attr("data-id")
if (seasonId.isNullOrBlank()) return@apmapIndexed
var episode = 0
val seasonEpisodes = app.get("$mainUrl/ajax/v2/season/episodes/$seasonId").document
var seasonEpisodesItems =
seasonEpisodes.select("div.flw-item.film_single-item.episode-item.eps-item")
if (seasonEpisodesItems.isNullOrEmpty()) {
seasonEpisodesItems =
seasonEpisodes.select("ul > li > a")
}
seasonEpisodesItems.forEach {
val episodeImg = it?.select("img")
val episodeTitle = episodeImg?.attr("title") ?: it.ownText()
val episodePosterUrl = episodeImg?.attr("src")
val episodeData = it.attr("data-id") ?: return@forEach
episode++
val episodeNum =
(it.select("div.episode-number").text()
?: episodeTitle).let { str ->
Regex("""\d+""").find(str)?.groupValues?.firstOrNull()
?.toIntOrNull()
} ?: episode
episodes.add(
newEpisode(Pair(url, episodeData)) {
this.posterUrl = fixUrlNull(episodePosterUrl)
this.name = episodeTitle?.removePrefix("Episode $episodeNum: ")
this.season = season + 1
this.episode = episodeNum
}
)
}
}
return newTvSeriesLoadResponse(title, url, TvType.TvSeries, episodes) {
this.posterUrl = posterUrl
this.year = year
this.plot = plot
addDuration(duration)
addActors(cast)
this.tags = tags
this.recommendations = recommendations
addTrailer(youtubeTrailer)
this.rating = rating
}
}
}
data class Tracks(
@JsonProperty("file") val file: String?,
@JsonProperty("label") val label: String?,
@JsonProperty("kind") val kind: String?
)
data class Sources(
@JsonProperty("file") val file: String?,
@JsonProperty("type") val type: String?,
@JsonProperty("label") val label: String?
)
data class SourceObject(
@JsonProperty("sources") val sources: List<Sources?>?,
@JsonProperty("sources_1") val sources1: List<Sources?>?,
@JsonProperty("sources_2") val sources2: List<Sources?>?,
@JsonProperty("sourcesBackup") val sourcesBackup: List<Sources?>?,
@JsonProperty("tracks") val tracks: List<Tracks?>?
)
data class IframeJson(
// @JsonProperty("type") val type: String? = null,
@JsonProperty("link") val link: String? = null,
// @JsonProperty("sources") val sources: ArrayList<String> = arrayListOf(),
// @JsonProperty("tracks") val tracks: ArrayList<String> = arrayListOf(),
// @JsonProperty("title") val title: String? = null
)
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
val urls = (tryParseJson<Pair<String, String>>(data)?.let { (prefix, server) ->
val episodesUrl = "$mainUrl/ajax/v2/episode/servers/$server"
// Supported streams, they're identical
app.get(episodesUrl).document.select("a").mapNotNull { element ->
val id = element?.attr("data-id") ?: return@mapNotNull null
if (element.select("span").text().trim().isValidServer()) {
"$prefix.$id".replace("/tv/", "/watch-tv/")
} else {
null
}
}
} ?: tryParseJson<List<String>>(data))?.distinct()
urls?.apmap { url ->
suspendSafeApiCall {
// Possible without token
// val response = app.get(url)
// val key =
// response.document.select("script[src*=https://www.google.com/recaptcha/api.js?render=]")
// .attr("src").substringAfter("render=")
// val token = getCaptchaToken(mainUrl, key) ?: return@suspendSafeApiCall
val serverId = url.substringAfterLast(".")
val iframeLink =
app.get("${this.mainUrl}/ajax/get_link/$serverId").parsed<IframeJson>().link
?: return@suspendSafeApiCall
// Some smarter ws11 or w10 selection might be required in the future.
val extractorData =
"https://ws11.rabbitstream.net/socket.io/?EIO=4&transport=polling"
if (iframeLink.contains("streamlare", ignoreCase = true)) {
loadExtractor(iframeLink, null, subtitleCallback, callback)
} else {
extractRabbitStream(iframeLink, subtitleCallback, callback, false) { it }
}
}
}
return !urls.isNullOrEmpty()
}
override suspend fun extractorVerifierJob(extractorData: String?) {
runSflixExtractorVerifierJob(this, extractorData, "https://rabbitstream.net/")
}
private fun Element.toSearchResult(): SearchResponse {
val inner = this.selectFirst("div.film-poster")
val img = inner!!.select("img")
val title = img.attr("title")
val posterUrl = img.attr("data-src") ?: img.attr("src")
val href = fixUrl(inner.select("a").attr("href"))
val isMovie = href.contains("/movie/")
val otherInfo =
this.selectFirst("div.film-detail > div.fd-infor")?.select("span")?.toList() ?: listOf()
//var rating: Int? = null
var year: Int? = null
var quality: SearchQuality? = null
when (otherInfo.size) {
1 -> {
year = otherInfo[0]?.text()?.trim()?.toIntOrNull()
}
2 -> {
year = otherInfo[0]?.text()?.trim()?.toIntOrNull()
}
3 -> {
//rating = otherInfo[0]?.text()?.toRatingInt()
quality = getQualityFromString(otherInfo[1]?.text())
year = otherInfo[2]?.text()?.trim()?.toIntOrNull()
}
}
return if (isMovie) {
MovieSearchResponse(
title,
href,
this@SflixProvider.name,
TvType.Movie,
posterUrl = posterUrl,
year = year,
quality = quality,
)
} else {
TvSeriesSearchResponse(
title,
href,
this@SflixProvider.name,
TvType.Movie,
posterUrl,
year = year,
episodes = null,
quality = quality,
)
}
}
companion object {
data class PollingData(
@JsonProperty("sid") val sid: String? = null,
@JsonProperty("upgrades") val upgrades: ArrayList<String> = arrayListOf(),
@JsonProperty("pingInterval") val pingInterval: Int? = null,
@JsonProperty("pingTimeout") val pingTimeout: Int? = null
)
/*
# python code to figure out the time offset based on code if necessary
chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_"
code = "Nxa_-bM"
total = 0
for i, char in enumerate(code[::-1]):
index = chars.index(char)
value = index * 64**i
total += value
print(f"total {total}")
*/
private fun generateTimeStamp(): String {
val chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_"
var code = ""
var time = unixTimeMS
while (time > 0) {
code += chars[(time % (chars.length)).toInt()]
time /= chars.length
}
return code.reversed()
}
/**
* Generates a session
* 1 Get request.
* */
private suspend fun negotiateNewSid(baseUrl: String): PollingData? {
// Tries multiple times
for (i in 1..5) {
val jsonText =
app.get("$baseUrl&t=${generateTimeStamp()}").text.replaceBefore("{", "")
// println("Negotiated sid $jsonText")
parseJson<PollingData?>(jsonText)?.let { return it }
delay(1000L * i)
}
return null
}
/**
* Generates a new session if the request fails
* @return the data and if it is new.
* */
private suspend fun getUpdatedData(
response: NiceResponse,
data: PollingData,
baseUrl: String
): Pair<PollingData, Boolean> {
if (!response.okhttpResponse.isSuccessful) {
return negotiateNewSid(baseUrl)?.let {
it to true
} ?: data to false
}
return data to false
}
private suspend fun initPolling(
extractorData: String,
referer: String
): Pair<PollingData?, String?> {
val headers = mapOf(
"Referer" to referer // "https://rabbitstream.net/"
)
val data = negotiateNewSid(extractorData) ?: return null to null
app.post(
"$extractorData&t=${generateTimeStamp()}&sid=${data.sid}",
requestBody = "40".toRequestBody(),
headers = headers
)
// This makes the second get request work, and re-connect work.
val reconnectSid =
parseJson<PollingData>(
app.get(
"$extractorData&t=${generateTimeStamp()}&sid=${data.sid}",
headers = headers
)
// .also { println("First get ${it.text}") }
.text.replaceBefore("{", "")
).sid
// This response is used in the post requests. Same contents in all it seems.
val authInt =
app.get(
"$extractorData&t=${generateTimeStamp()}&sid=${data.sid}",
timeout = 60,
headers = headers
).text
//.also { println("Second get ${it}") }
// Dunno if it's actually generated like this, just guessing.
.toIntOrNull()?.plus(1) ?: 3
return data to reconnectSid
}
suspend fun runSflixExtractorVerifierJob(
api: MainAPI,
extractorData: String?,
referer: String
) {
if (extractorData == null) return
val headers = mapOf(
"Referer" to referer // "https://rabbitstream.net/"
)
lateinit var data: PollingData
var reconnectSid = ""
initPolling(extractorData, referer)
.also {
data = it.first ?: throw RuntimeException("Data Null")
reconnectSid = it.second ?: throw RuntimeException("ReconnectSid Null")
}
// Prevents them from fucking us over with doing a while(true){} loop
val interval = maxOf(data.pingInterval?.toLong()?.plus(2000) ?: return, 10000L)
var reconnect = false
var newAuth = false
while (true) {
val authData =
when {
newAuth -> "40"
reconnect -> """42["_reconnect", "$reconnectSid"]"""
else -> "3"
}
val url = "${extractorData}&t=${generateTimeStamp()}&sid=${data.sid}"
getUpdatedData(
app.post(url, json = authData, headers = headers),
data,
extractorData
).also {
newAuth = it.second
data = it.first
}
//.also { println("Sflix post job ${it.text}") }
Log.d(api.name, "Running ${api.name} job $url")
val time = measureTimeMillis {
// This acts as a timeout
val getResponse = app.get(
url,
timeout = interval / 1000,
headers = headers
)
// .also { println("Sflix get job ${it.text}") }
reconnect = getResponse.text.contains("sid")
}
// Always waits even if the get response is instant, to prevent a while true loop.
if (time < interval - 4000)
delay(4000)
}
}
// Only scrape servers with these names
fun String?.isValidServer(): Boolean {
val list = listOf("upcloud", "vidcloud", "streamlare")
return list.contains(this?.lowercase(Locale.ROOT))
}
// For re-use in Zoro
private suspend fun Sources.toExtractorLink(
caller: MainAPI,
name: String,
extractorData: String? = null,
): List<ExtractorLink>? {
return this.file?.let { file ->
//println("FILE::: $file")
val isM3u8 = URI(this.file).path.endsWith(".m3u8") || this.type.equals(
"hls",
ignoreCase = true
)
return if (isM3u8) {
suspendSafeApiCall {
M3u8Helper().m3u8Generation(
M3u8Helper.M3u8Stream(
this.file,
null,
mapOf("Referer" to "https://mzzcloud.life/")
), false
)
.map { stream ->
ExtractorLink(
caller.name,
"${caller.name} $name",
stream.streamUrl,
caller.mainUrl,
getQualityFromName(stream.quality?.toString()),
true,
extractorData = extractorData
)
}
} ?: listOf(
// Fallback if m3u8 extractor fails
ExtractorLink(
caller.name,
"${caller.name} $name",
this.file,
caller.mainUrl,
getQualityFromName(this.label),
isM3u8,
extractorData = extractorData
)
)
} else {
listOf(
ExtractorLink(
caller.name,
caller.name,
file,
caller.mainUrl,
getQualityFromName(this.label),
false,
extractorData = extractorData
)
)
}
}
}
private fun Tracks.toSubtitleFile(): SubtitleFile? {
return this.file?.let {
SubtitleFile(
this.label ?: "Unknown",
it
)
}
}
suspend fun MainAPI.extractRabbitStream(
url: String,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit,
useSidAuthentication: Boolean,
/** Used for extractorLink name, input: Source name */
extractorData: String? = null,
nameTransformer: (String) -> String,
) = suspendSafeApiCall {
// https://rapid-cloud.ru/embed-6/dcPOVRE57YOT?z= -> https://rapid-cloud.ru/embed-6
val mainIframeUrl =
url.substringBeforeLast("/")
val mainIframeId = url.substringAfterLast("/")
.substringBefore("?") // https://rapid-cloud.ru/embed-6/dcPOVRE57YOT?z= -> dcPOVRE57YOT
val iframe = app.get(url, referer = mainUrl)
val iframeKey =
iframe.document.select("script[src*=https://www.google.com/recaptcha/api.js?render=]")
.attr("src").substringAfter("render=")
val iframeToken = getCaptchaToken(url, iframeKey)
val number =
Regex("""recaptchaNumber = '(.*?)'""").find(iframe.text)?.groupValues?.get(1)
var sid: String? = null
if (useSidAuthentication && extractorData != null) {
negotiateNewSid(extractorData)?.also { pollingData ->
app.post(
"$extractorData&t=${generateTimeStamp()}&sid=${pollingData.sid}",
requestBody = "40".toRequestBody(),
timeout = 60
)
val text = app.get(
"$extractorData&t=${generateTimeStamp()}&sid=${pollingData.sid}",
timeout = 60
).text.replaceBefore("{", "")
sid = parseJson<PollingData>(text).sid
ioSafe { app.get("$extractorData&t=${generateTimeStamp()}&sid=${pollingData.sid}") }
}
}
val mapped = app.get(
"${
mainIframeUrl.replace(
"/embed",
"/ajax/embed"
)
}/getSources?id=$mainIframeId&_token=$iframeToken&_number=$number${sid?.let { "$&sId=$it" } ?: ""}",
referer = mainUrl,
headers = mapOf(
"X-Requested-With" to "XMLHttpRequest",
"Accept" to "*/*",
"Accept-Language" to "en-US,en;q=0.5",
// "Cache-Control" to "no-cache",
"Connection" to "keep-alive",
// "Sec-Fetch-Dest" to "empty",
// "Sec-Fetch-Mode" to "no-cors",
// "Sec-Fetch-Site" to "cross-site",
// "Pragma" to "no-cache",
// "Cache-Control" to "no-cache",
"TE" to "trailers"
)
).parsed<SourceObject>()
mapped.tracks?.forEach { track ->
track?.toSubtitleFile()?.let { subtitleFile ->
subtitleCallback.invoke(subtitleFile)
}
}
val list = listOf(
mapped.sources to "source 1",
mapped.sources1 to "source 2",
mapped.sources2 to "source 3",
mapped.sourcesBackup to "source backup"
)
list.forEach { subList ->
subList.first?.forEach { source ->
source?.toExtractorLink(
this,
nameTransformer(subList.second),
extractorData,
)
?.forEach {
// Sets Zoro SID used for video loading
// (this as? ZoroProvider)?.sid?.set(it.url.hashCode(), sid)
callback(it)
}
}
}
}
}
}

View File

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

View File

@ -1,6 +0,0 @@
package com.lagradost
class SolarmovieProvider : SflixProvider() {
override var mainUrl = "https://solarmovie.pe"
override var name = "Solarmovie"
}

View File

@ -1,79 +0,0 @@
package com.lagradost
import android.util.Log
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.SflixProvider.Companion.extractRabbitStream
import com.lagradost.SflixProvider.Companion.runSflixExtractorVerifierJob
import com.lagradost.cloudstream3.APIHolder.getCaptchaToken
import com.lagradost.cloudstream3.SubtitleFile
import com.lagradost.cloudstream3.TvType
import com.lagradost.cloudstream3.apmap
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.metaproviders.TmdbLink
import com.lagradost.cloudstream3.metaproviders.TmdbProvider
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.loadExtractor
class TwoEmbedProvider : TmdbProvider() {
override val apiName = "2Embed"
override var name = "2Embed"
override var mainUrl = "https://www.2embed.to"
override val useMetaLoadResponse = true
override val instantLinkLoading = false
override val supportedTypes = setOf(
TvType.Movie,
TvType.TvSeries,
)
data class EmbedJson (
@JsonProperty("type") val type: String?,
@JsonProperty("link") val link: String,
@JsonProperty("sources") val sources: List<String?>,
@JsonProperty("tracks") val tracks: List<String>?
)
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
val mappedData = parseJson<TmdbLink>(data)
val (id, site) = if (mappedData.imdbID != null) listOf(
mappedData.imdbID,
"imdb"
) else listOf(mappedData.tmdbID.toString(), "tmdb")
val isMovie = mappedData.episode == null && mappedData.season == null
val embedUrl = if (isMovie) {
"$mainUrl/embed/$site/movie?id=$id"
} else {
val suffix = "$id&s=${mappedData.season ?: 1}&e=${mappedData.episode ?: 1}"
"$mainUrl/embed/$site/tv?id=$suffix"
}
val document = app.get(embedUrl).document
val captchaKey =
document.select("script[src*=https://www.google.com/recaptcha/api.js?render=]")
.attr("src").substringAfter("render=")
val servers = document.select(".dropdown-menu a[data-id]").map { it.attr("data-id") }
servers.apmap { serverID ->
val token = getCaptchaToken(embedUrl, captchaKey)
val ajax = app.get("$mainUrl/ajax/embed/play?id=$serverID&_token=$token", referer = embedUrl).text
val mappedservers = parseJson<EmbedJson>(ajax)
val iframeLink = mappedservers.link
if (iframeLink.contains("rabbitstream")) {
extractRabbitStream(iframeLink, subtitleCallback, callback, false) { it }
} else {
loadExtractor(iframeLink, embedUrl, subtitleCallback, callback)
}
}
return true
}
override suspend fun extractorVerifierJob(extractorData: String?) {
Log.d(this.name, "Starting ${this.name} job!")
runSflixExtractorVerifierJob(this, extractorData, "https://rabbitstream.net/")
}
}

View File

@ -1,371 +0,0 @@
package com.lagradost
import android.util.Log
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.SflixProvider.Companion.extractRabbitStream
import com.lagradost.SflixProvider.Companion.runSflixExtractorVerifierJob
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.LoadResponse.Companion.addAniListId
import com.lagradost.cloudstream3.LoadResponse.Companion.addMalId
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.loadExtractor
import com.lagradost.nicehttp.Requests.Companion.await
import okhttp3.Interceptor
import org.jsoup.Jsoup
import org.jsoup.nodes.Element
import java.net.URI
private const val OPTIONS = "OPTIONS"
class ZoroProvider : MainAPI() {
override var mainUrl = "https://zoro.to"
override var name = "Zoro"
override val hasQuickSearch = false
override val hasMainPage = true
override val hasChromecastSupport = true
override val hasDownloadSupport = true
override val usesWebView = 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
}
}
}
val epRegex = Regex("Ep (\\d+)/")
private fun Element.toSearchResult(): SearchResponse? {
val href = fixUrl(this.select("a").attr("href"))
val title = this.select("h3.film-name").text()
val dubSub = this.select(".film-poster > .tick.ltr").text()
//val episodes = this.selectFirst(".film-poster > .tick-eps")?.text()?.toIntOrNull()
val dubExist = dubSub.contains("dub", ignoreCase = true)
val subExist = dubSub.contains("sub", ignoreCase = true)
val episodes =
this.selectFirst(".film-poster > .tick.rtl > .tick-eps")?.text()?.let { eps ->
//println("REGEX:::: $eps")
// current episode / max episode
//Regex("Ep (\\d+)/(\\d+)")
epRegex.find(eps)?.groupValues?.get(1)?.toIntOrNull()
}
if (href.contains("/news/") || title.trim().equals("News", ignoreCase = true)) return null
val posterUrl = fixUrl(this.select("img").attr("data-src"))
val type = getType(this.select("div.fd-infor > span.fdi-item").text())
return newAnimeSearchResponse(title, href, type) {
this.posterUrl = posterUrl
addDubStatus(dubExist, subExist, episodes, episodes)
}
}
override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse {
val html = app.get("$mainUrl/home").text
val document = Jsoup.parse(html)
val homePageList = ArrayList<HomePageList>()
document.select("div.anif-block").forEach { block ->
val header = block.select("div.anif-block-header").text().trim()
val animes = block.select("li").mapNotNull {
it.toSearchResult()
}
if (animes.isNotEmpty()) homePageList.add(HomePageList(header, animes))
}
document.select("section.block_area.block_area_home").forEach { block ->
val header = block.select("h2.cat-heading").text().trim()
val animes = block.select("div.flw-item").mapNotNull {
it.toSearchResult()
}
if (animes.isNotEmpty()) homePageList.add(HomePageList(header, animes))
}
return HomePageResponse(homePageList)
}
private data class Response(
@JsonProperty("status") val status: Boolean,
@JsonProperty("html") val html: String
)
// override suspend fun quickSearch(query: String): List<SearchResponse> {
// val url = "$mainUrl/ajax/search/suggest?keyword=${query}"
// val html = mapper.readValue<Response>(khttp.get(url).text).html
// val document = Jsoup.parse(html)
//
// return document.select("a.nav-item").map {
// val title = it.selectFirst(".film-name")?.text().toString()
// val href = fixUrl(it.attr("href"))
// val year = it.selectFirst(".film-infor > span")?.text()?.split(",")?.get(1)?.trim()?.toIntOrNull()
// val image = it.select("img").attr("data-src")
//
// AnimeSearchResponse(
// title,
// href,
// this.name,
// TvType.TvSeries,
// image,
// year,
// null,
// EnumSet.of(DubStatus.Subbed),
// null,
// null
// )
//
// }
// }
override suspend fun search(query: String): List<SearchResponse> {
val link = "$mainUrl/search?keyword=$query"
val html = app.get(link).text
val document = Jsoup.parse(html)
return document.select(".flw-item").map {
val title = it.selectFirst(".film-detail > .film-name > a")?.attr("title").toString()
val filmPoster = it.selectFirst(".film-poster")
val poster = filmPoster!!.selectFirst("img")?.attr("data-src")
val episodes = filmPoster.selectFirst("div.rtl > div.tick-eps")?.text()?.let { eps ->
// current episode / max episode
val epRegex = Regex("Ep (\\d+)/")//Regex("Ep (\\d+)/(\\d+)")
epRegex.find(eps)?.groupValues?.get(1)?.toIntOrNull()
}
val dubsub = filmPoster.selectFirst("div.ltr")?.text()
val dubExist = dubsub?.contains("DUB") ?: false
val subExist = dubsub?.contains("SUB") ?: false || dubsub?.contains("RAW") ?: false
val tvType =
getType(it.selectFirst(".film-detail > .fd-infor > .fdi-item")?.text().toString())
val href = fixUrl(it.selectFirst(".film-name a")!!.attr("href"))
newAnimeSearchResponse(title, href, tvType) {
this.posterUrl = poster
addDubStatus(dubExist, subExist, episodes, episodes)
}
}
}
private fun Element?.getActor(): Actor? {
val image =
fixUrlNull(this?.selectFirst(".pi-avatar > img")?.attr("data-src")) ?: return null
val name = this?.selectFirst(".pi-detail > .pi-name")?.text() ?: return null
return Actor(name = name, image = image)
}
data class ZoroSyncData(
@JsonProperty("mal_id") val malId: String?,
@JsonProperty("anilist_id") val aniListId: String?,
)
override suspend fun load(url: String): LoadResponse {
val html = app.get(url).text
val document = Jsoup.parse(html)
val syncData = tryParseJson<ZoroSyncData>(document.selectFirst("#syncData")?.data())
val title = document.selectFirst(".anisc-detail > .film-name")?.text().toString()
val poster = document.selectFirst(".anisc-poster img")?.attr("src")
val tags = document.select(".anisc-info a[href*=\"/genre/\"]").map { it.text() }
var year: Int? = null
var japaneseTitle: String? = null
var status: ShowStatus? = null
for (info in document.select(".anisc-info > .item.item-title")) {
val text = info?.text().toString()
when {
(year != null && japaneseTitle != null && status != null) -> break
text.contains("Premiered") && year == null ->
year =
info.selectFirst(".name")?.text().toString().split(" ").last().toIntOrNull()
text.contains("Japanese") && japaneseTitle == null ->
japaneseTitle = info.selectFirst(".name")?.text().toString()
text.contains("Status") && status == null ->
status = getStatus(info.selectFirst(".name")?.text().toString())
}
}
val description = document.selectFirst(".film-description.m-hide > .text")?.text()
val animeId = URI(url).path.split("-").last()
val episodes = Jsoup.parse(
parseJson<Response>(
app.get(
"$mainUrl/ajax/v2/episode/list/$animeId"
).text
).html
).select(".ss-list > a[href].ssl-item.ep-item").map {
newEpisode(it.attr("href")) {
this.name = it?.attr("title")
this.episode = it.selectFirst(".ssli-order")?.text()?.toIntOrNull()
}
}
val actors = document.select("div.block-actors-content > div.bac-list-wrap > div.bac-item")
.mapNotNull { head ->
val subItems = head.select(".per-info") ?: return@mapNotNull null
if (subItems.isEmpty()) return@mapNotNull null
var role: ActorRole? = null
val mainActor = subItems.first()?.let {
role = when (it.selectFirst(".pi-detail > .pi-cast")?.text()?.trim()) {
"Supporting" -> ActorRole.Supporting
"Main" -> ActorRole.Main
else -> null
}
it.getActor()
} ?: return@mapNotNull null
val voiceActor = if (subItems.size >= 2) subItems[1]?.getActor() else null
ActorData(actor = mainActor, role = role, voiceActor = voiceActor)
}
val recommendations =
document.select("#main-content > section > .tab-content > div > .film_list-wrap > .flw-item")
.mapNotNull { head ->
val filmPoster = head?.selectFirst(".film-poster")
val epPoster = filmPoster?.selectFirst("img")?.attr("data-src")
val a = head?.selectFirst(".film-detail > .film-name > a")
val epHref = a?.attr("href")
val epTitle = a?.attr("title")
if (epHref == null || epTitle == null || epPoster == null) {
null
} else {
AnimeSearchResponse(
epTitle,
fixUrl(epHref),
this.name,
TvType.Anime,
epPoster,
dubStatus = null
)
}
}
return newAnimeLoadResponse(title, url, TvType.Anime) {
japName = japaneseTitle
engName = title
posterUrl = poster
this.year = year
addEpisodes(DubStatus.Subbed, episodes)
showStatus = status
plot = description
this.tags = tags
this.recommendations = recommendations
this.actors = actors
addMalId(syncData?.malId?.toIntOrNull())
addAniListId(syncData?.aniListId?.toIntOrNull())
}
}
private data class RapidCloudResponse(
@JsonProperty("link") val link: String
)
override suspend fun extractorVerifierJob(extractorData: String?) {
Log.d(this.name, "Starting ${this.name} job!")
runSflixExtractorVerifierJob(this, extractorData, "https://rapid-cloud.ru/")
}
/** Url hashcode to sid */
var sid: HashMap<Int, String?> = hashMapOf()
/**
* Makes an identical Options request before .ts request
* Adds an SID header to the .ts request.
* */
override fun getVideoInterceptor(extractorLink: ExtractorLink): Interceptor {
// Needs to be object instead of lambda to make it compile correctly
return object : Interceptor {
override fun intercept(chain: Interceptor.Chain): okhttp3.Response {
val request = chain.request()
if (request.url.toString().endsWith(".ts")
&& request.method != OPTIONS
// No option requests on VidCloud
&& !request.url.toString().contains("betterstream")
) {
val newRequest =
chain.request()
.newBuilder().apply {
sid[extractorLink.url.hashCode()]?.let { sid ->
addHeader("SID", sid)
}
}
.build()
val options = request.newBuilder().method(OPTIONS, request.body).build()
ioSafe { app.baseClient.newCall(options).await() }
return chain.proceed(newRequest)
} else {
return chain.proceed(chain.request())
}
}
}
}
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
val servers: List<Pair<DubStatus, String>> = Jsoup.parse(
app.get("$mainUrl/ajax/v2/episode/servers?episodeId=" + data.split("=")[1])
.parsed<Response>().html
).select(".server-item[data-type][data-id]").map {
Pair(
if (it.attr("data-type") == "sub") DubStatus.Subbed else DubStatus.Dubbed,
it.attr("data-id")
)
}
val extractorData =
"https://ws1.rapid-cloud.ru/socket.io/?EIO=4&transport=polling"
// Prevent duplicates
servers.distinctBy { it.second }.apmap {
val link =
"$mainUrl/ajax/v2/episode/sources?id=${it.second}"
val extractorLink = app.get(
link,
).parsed<RapidCloudResponse>().link
val hasLoadedExtractorLink =
loadExtractor(extractorLink, "https://rapid-cloud.ru/", subtitleCallback, callback)
if (!hasLoadedExtractorLink) {
extractRabbitStream(
extractorLink,
subtitleCallback,
// Blacklist VidCloud for now
{ videoLink -> if (!videoLink.url.contains("betterstream")) callback(videoLink) },
true,
extractorData
) { sourceName ->
sourceName + " - ${it.first}"
}
}
}
return true
}
}

View File

@ -1,25 +0,0 @@
// 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
tvTypes = listOf(
"TvSeries",
"Movie",
)
iconUrl = "https://www.google.com/s2/favicons?domain=secretlink.xyz&sz=24"
}

View File

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

View File

@ -1,263 +0,0 @@
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 org.jsoup.Jsoup
class SoaptwoDayProvider : MainAPI() {
override var mainUrl = "https://secretlink.xyz" //Probably a rip off, but it has no captcha
override var name = "Soap2Day"
override val hasMainPage = true
override val hasChromecastSupport = true
override val hasDownloadSupport = true
override val supportedTypes = setOf(
TvType.Movie,
TvType.TvSeries,
)
override val mainPage = mainPageOf(
Pair("$mainUrl/movielist?page=", "Movies"),
Pair("$mainUrl/tvlist?page=", "TV Series"),
)
override suspend fun getMainPage(
page: Int,
request : MainPageRequest
): HomePageResponse {
val url = request.data + page
val soup = app.get(url).document
val home =
soup.select("div.container div.row div.col-sm-12.col-lg-12 div.row div.col-sm-12.col-lg-12 .col-xs-6")
.map {
val title = it.selectFirst("h5 a")!!.text()
val link = it.selectFirst("a")!!.attr("href")
TvSeriesSearchResponse(
title,
link,
this.name,
TvType.TvSeries,
fixUrl(it.selectFirst("img")!!.attr("src")),
null,
null,
)
}
return newHomePageResponse(request.name, home)
}
override suspend fun search(query: String): List<SearchResponse> {
val doc = app.get("$mainUrl/search/keyword/$query").document
return doc.select("div.container div.row div.col-sm-12.col-lg-12 div.row div.col-sm-12.col-lg-12 .col-xs-6")
.map {
val title = it.selectFirst("h5 a")!!.text()
val image = fixUrl(it.selectFirst("img")!!.attr("src"))
val href = fixUrl(it.selectFirst("a")!!.attr("href"))
TvSeriesSearchResponse(
title,
href,
this.name,
TvType.TvSeries,
image,
null,
null
)
}
}
override suspend fun load(url: String): LoadResponse? {
val soup = app.get(url).document
val title = soup.selectFirst(".hidden-lg > div:nth-child(1) > h4")?.text() ?: ""
val description = soup.selectFirst("p#wrap")?.text()?.trim()
val poster =
soup.selectFirst(".col-md-5 > div:nth-child(1) > div:nth-child(1) > img")?.attr("src")
val episodes = mutableListOf<Episode>()
soup.select("div.alert").forEach {
val season = it?.selectFirst("h4")?.text()?.filter { c -> c.isDigit() }?.toIntOrNull()
it?.select("div > div > a")?.forEach { entry ->
val link = fixUrlNull(entry?.attr("href")) ?: return@forEach
val text = entry?.text() ?: ""
val name = text.replace(Regex("(^(\\d+)\\.)"), "")
val epNum = text.substring(0, text.indexOf(".")).toIntOrNull()
episodes.add(
Episode(
name = name,
data = link,
season = season,
episode = epNum
)
)
}
}
val otherInfoBody = soup.select("div.col-sm-8 div.panel-body").toString()
//Fetch casts
val casts = otherInfoBody.substringAfter("Stars : ")
.substringBefore("Genre : ").let {
Jsoup.parse(it).select("a")
}.mapNotNull {
val castName = it?.text() ?: return@mapNotNull null
ActorData(
Actor(
name = castName
)
)
}
//Fetch year
val year = otherInfoBody.substringAfter("<h4>Release : </h4>")
.substringBefore("<div").let {
//Log.i(this.name, "Result => year string: $it")
Jsoup.parse(it).select("p")[1]
}?.text()?.take(4)?.toIntOrNull()
//Fetch genres
val genre = otherInfoBody.substringAfter("<h4>Genre : </h4>")
.substringBefore("<h4>Release : </h4>").let {
//Log.i(this.name, "Result => genre string: $it")
Jsoup.parse(it).select("a")
}.mapNotNull { it?.text()?.trim() ?: return@mapNotNull null }
return when (val tvType = if (episodes.isEmpty()) TvType.Movie else TvType.TvSeries) {
TvType.TvSeries -> {
TvSeriesLoadResponse(
title,
url,
this.name,
tvType,
episodes.reversed(),
fixUrlNull(poster),
year = year,
description,
actors = casts,
tags = genre
)
}
TvType.Movie -> {
MovieLoadResponse(
title,
url,
this.name,
tvType,
url,
fixUrlNull(poster),
year = year,
description,
actors = casts,
tags = genre
)
}
else -> null
}
}
data class ServerJson(
@JsonProperty("0") val zero: String?,
@JsonProperty("key") val key: Boolean?,
@JsonProperty("val") val stream: String?,
@JsonProperty("val_bak") val streambackup: String?,
@JsonProperty("pos") val pos: Int?,
@JsonProperty("type") val type: String?,
@JsonProperty("subs") val subs: List<Subs>?,
@JsonProperty("prev_epi_title") val prevEpiTitle: String?,
@JsonProperty("prev_epi_url") val prevEpiUrl: String?,
@JsonProperty("next_epi_title") val nextEpiTitle: String?,
@JsonProperty("next_epi_url") val nextEpiUrl: String?
)
data class Subs(
@JsonProperty("id") val id: Int?,
@JsonProperty("movieId") val movieId: Int?,
@JsonProperty("tvId") val tvId: Int?,
@JsonProperty("episodeId") val episodeId: Int?,
@JsonProperty("default") val default: Int?,
@JsonProperty("IsShow") val IsShow: Int?,
@JsonProperty("name") val name: String,
@JsonProperty("path") val path: String?,
@JsonProperty("downlink") val downlink: String?,
@JsonProperty("source_file_name") val sourceFileName: String?,
@JsonProperty("createtime") val createtime: Int?
)
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
val doc = app.get(data).document
val idplayer = doc.selectFirst("#divU")?.text()
val idplayer2 = doc.selectFirst("#divP")?.text()
val movieid = doc.selectFirst("div.row input#hId")!!.attr("value")
val tvType = try {
doc.selectFirst(".col-md-5 > div:nth-child(1) > div:nth-child(1) > img")!!.attr("src")
?: ""
} catch (e: Exception) {
""
}
val ajaxlink =
if (tvType.contains("movie")) "$mainUrl/home/index/GetMInfoAjax" else "$mainUrl/home/index/GetEInfoAjax"
listOf(
idplayer,
idplayer2,
).mapNotNull { playerID ->
val url = app.post(
ajaxlink,
headers = mapOf(
"Host" to "secretlink.xyz",
"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://secretlink.xyz",
"DNT" to "1",
"Connection" to "keep-alive",
"Referer" to data,
"Sec-Fetch-Dest" to "empty",
"Sec-Fetch-Mode" to "cors",
"Sec-Fetch-Site" to "same-origin",
),
data = mapOf(
Pair("pass", movieid),
Pair("param", playerID ?: ""),
)
).text.replace("\\\"", "\"").replace("\"{", "{").replace("}\"", "}")
.replace("\\\\\\/", "\\/")
val json = parseJson<ServerJson>(url)
listOfNotNull(
json.stream,
json.streambackup
).apmap { stream ->
val cleanstreamurl = stream.replace("\\/", "/").replace("\\\\\\", "")
if (cleanstreamurl.isNotBlank()) {
callback(
ExtractorLink(
"Soap2Day",
"Soap2Day",
cleanstreamurl,
"https://soap2day.ac",
Qualities.Unknown.value,
isM3u8 = false
)
)
}
}
json.subs?.forEach { subtitle ->
val sublink = mainUrl + subtitle.path
listOf(
sublink,
subtitle.downlink
).mapNotNull { subs ->
if (subs != null) {
if (subs.isNotBlank()) {
subtitleCallback(
SubtitleFile(subtitle.name, subs)
)
}
}
}
}
}
return true
}
}

View File

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

View File

@ -1,26 +0,0 @@
// 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
tvTypes = listOf(
"Anime",
"Movie",
"AnimeMovie",
"TvSeries",
)
iconUrl = "https://raw.githubusercontent.com/recloudstream/cloudstream-extensions/master/SuperStream/icon.png"
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

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