New Providers [Vietnamese][Ukrainian][Russian] (#1266)

* Add files via upload

* Add files via upload

* Update MainAPI.kt

* - Add NontonAnimeID and Kuramanime
 - refactoring code

* add GomunimeProvider, add some source, fix minor code

* add sources

* add KuronimeProvider, add sources

* small fix, (ready to merge..)

* add indonesia provider, add extractor, add source, small fix

* small fix (ready to merge)

* fix

* fix layarkaca/gomunime/kuronime

* fix (ready to merge)

* add new indonesian providers

* small fix

* add multiplex provider

* update providers

* add trailer to Providers

* add indexsubtitle

* small fix

* small fix (ready to merge)

* clean

* fix search

* improve search and load

* small fix

* left

* left too

* idlix domain fix

* fix AnimeIndo

* small cleaning

* fix(from feedback) & update Kuronime

* small fix

* fix

* fix extractor

* fix Kuronime source

* add HDrezka/Phimmoichill/Uakino

* fixHDrezka qualty

* try to fix Phimmoichill

* fix NontonAnimeID/Oploverz/Kuramanime & add extractor

* small fix

* enable indexsubtitle

* fixes HDrezka sources

* fixes rebahin

* fixes Idlix domain

* small fix

Co-authored-by: Osten <11805592+LagradOst@users.noreply.github.com>
This commit is contained in:
Hexated 2022-07-16 22:10:11 +07:00 committed by GitHub
parent 524d0717e2
commit b970ca0668
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 978 additions and 142 deletions

View file

@ -101,6 +101,10 @@ object APIHolder {
IdlixProvider(),
MultiplexProvider(),
VidSrcProvider(),
UakinoProvider(),
PhimmoichillProvider(),
HDrezkaProvider(),
// Metadata providers
//TmdbProvider(),

View file

@ -1,6 +1,7 @@
package com.lagradost.cloudstream3.animeproviders
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall
import com.lagradost.cloudstream3.network.DdosGuardKiller
import com.lagradost.cloudstream3.utils.ExtractorLink
import org.jsoup.Jsoup
@ -42,30 +43,22 @@ class KuramanimeProvider : MainAPI() {
val homePageList = ArrayList<HomePageList>()
document.select("div[class*=__product]").forEach { block ->
val header = block.select(".section-title > h4").text()
val animes = block.select("div.col-lg-4.col-md-6.col-sm-6").mapNotNull {
document.select("div.trending__product").forEach { block ->
val header = block.selectFirst("h4")?.text().toString()
val animes = block.select("div.col-lg-4.col-md-6.col-sm-6").map {
it.toSearchResult()
}
if (animes.isNotEmpty()) homePageList.add(HomePageList(header, animes))
}
document.select("#topAnimesSection").forEach { block ->
val header = block.previousElementSibling()!!.select("h5").text().trim()
val animes = block.select("a").mapNotNull {
document.select("div.product__sidebar__view").forEach { block ->
val header = block.selectFirst("h5")?.text().toString()
val animes = block.select("div.product__sidebar__comment__item").map {
it.toSearchResultView()
}
if (animes.isNotEmpty()) homePageList.add(HomePageList(header, animes))
}
document.select("#latestCommentSection").forEach { block ->
val header = block.previousElementSibling()!!.select("h5").text().trim()
val animes = block.select(".product__sidebar__comment__item").mapNotNull {
it.toSearchResultComment()
}
if (animes.isNotEmpty()) homePageList.add(HomePageList(header, animes))
}
return HomePageResponse(homePageList)
}
@ -77,41 +70,34 @@ class KuramanimeProvider : MainAPI() {
}
}
private fun Element.toSearchResult(): SearchResponse {
private fun Element.toSearchResult(): AnimeSearchResponse {
val href = getProperAnimeLink(fixUrl(this.selectFirst("a")!!.attr("href")))
val title = this.select(".product__item__text > h5 > a").text()
val posterUrl = fixUrl(this.select(".product__item__pic.set-bg").attr("data-setbg"))
val type = getType(this.selectFirst(".product__item__text > ul > li")!!.text())
return newAnimeSearchResponse(title, href, type) {
this.posterUrl = posterUrl
addDubStatus(dubExist = false, subExist = true)
}
}
private fun Element.toSearchResultView(): SearchResponse {
val href = getProperAnimeLink(fixUrl(this.attr("href")))
val title = this.selectFirst("h5")!!.text().trim()
val posterUrl =
fixUrl(this.select(".product__sidebar__view__item.set-bg").attr("data-setbg"))
val title = this.selectFirst("h5 a")?.text().toString()
val posterUrl = fixUrl(this.select("div.product__item__pic.set-bg").attr("data-setbg"))
val episode = Regex("([0-9*])\\s?/").find(
this.select("div.ep span").text()
)?.groupValues?.getOrNull(1)?.toIntOrNull()
return newAnimeSearchResponse(title, href, TvType.Anime) {
this.posterUrl = posterUrl
addDubStatus(dubExist = false, subExist = true)
addSub(episode)
}
}
private fun Element.toSearchResultComment(): SearchResponse {
val href = getProperAnimeLink(fixUrl(this.selectFirst("a")!!.attr("href")))
val title = this.selectFirst("h5")!!.text()
val posterUrl = fixUrl(this.select("img").attr("src"))
val type = getType(this.selectFirst("ul > li")!!.text())
private fun Element.toSearchResultView(): AnimeSearchResponse {
val href = getProperAnimeLink(fixUrl(this.selectFirst("a")?.attr("href").toString()))
val title = this.selectFirst("h5")?.text()?.trim().toString()
val posterUrl = fixUrlNull(
this.selectFirst("div.product__sidebar__comment__item__pic.set-bg")?.attr("data-setbg")
)
val episode =
this.selectFirst("h5")?.nextElementSibling()?.text()?.replace(Regex("[^0-9]"), "")
?.toIntOrNull()
return newAnimeSearchResponse(title, href, type) {
return newAnimeSearchResponse(title, href, TvType.Anime) {
this.posterUrl = posterUrl
addDubStatus(dubExist = false, subExist = true)
addSub(episode)
}
}
@ -190,8 +176,9 @@ class KuramanimeProvider : MainAPI() {
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
val servers = app.get(data, interceptor = DdosGuardKiller(true)).document
val servers = app.get(data).document
servers.select("video#player > source").map {
suspendSafeApiCall {
val url = it.attr("src")
val quality = it.attr("size").toInt()
callback.invoke(
@ -204,6 +191,7 @@ class KuramanimeProvider : MainAPI() {
)
)
}
}
return true
}

View file

@ -68,16 +68,26 @@ class NontonAnimeIDProvider : MainAPI() {
return if (uri.contains("/anime/")) {
uri
} else {
val name = Regex("$mainUrl/(.*)-episode.*").find(uri)?.groupValues?.get(1).toString()
if (name.contains("movie")) {
return "$mainUrl/anime/" + name.replace("-movie", "")
} else {
if (name.contains("kokurasetai-season-3")) {
"$mainUrl/anime/${name.replace("season-3", "ultra-romantic")}"
} else {
"$mainUrl/anime/$name"
}
var title = uri.substringAfter("$mainUrl/")
val fixTitle = Regex("(.*)-episode.*").find(title)?.groupValues?.getOrNull(1).toString()
title = when {
title.contains("utawarerumono-season-3") -> fixTitle.replace(
"season-3",
"futari-no-hakuoro"
)
title.contains("kingdom-season-4") -> fixTitle.replace("season-4", "4th-season")
title.contains("maou-sama-season-2") -> fixTitle.replace("season-2", "2")
title.contains("overlord-season-4") -> fixTitle.replace("season-4", "iv")
title.contains("kyoushitsu-e-season-2") -> fixTitle.replace(
"kyoushitsu-e-season-2",
"kyoushitsu-e-tv-2nd-season"
)
title.contains("season-2") -> fixTitle.replace("season-2", "2nd-season")
title.contains("season-3") -> fixTitle.replace("season-3", "3rd-season")
title.contains("movie") -> title.substringBefore("-movie")
else -> fixTitle
}
"$mainUrl/anime/$title"
}
}

View file

@ -69,6 +69,7 @@ class OploverzProvider : MainAPI() {
)?.groupValues?.get(1).toString()
(title.contains("-ova")) -> Regex("(.+)-ova").find(title)?.groupValues?.get(1)
.toString()
(title.contains("-movie")) -> Regex("(.+)-subtitle").find(title)?.groupValues?.get(1).toString()
else -> Regex("(.+)-subtitle").find(title)?.groupValues?.get(1).toString()
.replace(Regex("-\\d+"), "")
}
@ -136,9 +137,9 @@ class OploverzProvider : MainAPI() {
val typeCheck =
when (document.select(".info-content > .spe > span:nth-child(5), .info-content > .spe > span")
.text().trim()) {
"TV" -> "TV"
"OVA" -> "OVA"
"Movie" -> "Movie"
else -> "OVA"
else -> "TV"
}
val type = getType(typeCheck)
val description = document.select(".entry-content > p").text().trim()

View file

@ -0,0 +1,46 @@
package com.lagradost.cloudstream3.extractors
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.getQualityFromName
class Linkbox : ExtractorApi() {
override val name = "Linkbox"
override val mainUrl = "https://www.linkbox.to"
override val requiresReferer = true
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink> {
val id = url.substringAfter("id=")
val sources = mutableListOf<ExtractorLink>()
app.get("$mainUrl/api/open/get_url?itemId=$id", referer=url).parsedSafe<Responses>()?.data?.rList?.map { link ->
sources.add(
ExtractorLink(
name,
name,
link.url,
url,
getQualityFromName(link.resolution)
)
)
}
return sources
}
data class RList(
@JsonProperty("url") val url: String,
@JsonProperty("resolution") val resolution: String?,
)
data class Data(
@JsonProperty("rList") val rList: List<RList>?,
)
data class Responses(
@JsonProperty("data") val data: Data?,
)
}

View file

@ -0,0 +1,410 @@
package com.lagradost.cloudstream3.movieproviders
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.LoadResponse.Companion.addActors
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
import com.lagradost.cloudstream3.mvvm.safeApiCall
import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall
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.Qualities
import com.lagradost.cloudstream3.utils.getQualityFromName
import org.jsoup.Jsoup
import org.jsoup.nodes.Element
import java.util.*
class HDrezkaProvider : MainAPI() {
override var mainUrl = "https://rezka.ag"
override var name = "HDrezka"
override val hasMainPage = true
override var lang = "ru"
override val hasDownloadSupport = true
override val supportedTypes = setOf(
TvType.Movie,
TvType.TvSeries,
TvType.Anime,
TvType.AsianDrama
)
override suspend fun getMainPage(): HomePageResponse {
val items = ArrayList<HomePageList>()
listOf(
Pair("фильмы", "$mainUrl/films/?filter=watching"),
Pair("сериалы", "$mainUrl/series/?filter=watching"),
Pair("мультфильмы", "$mainUrl/cartoons/?filter=watching"),
Pair("аниме", "$mainUrl/animation/?filter=watching"),
).apmap { (header, url) ->
safeApiCall {
val home = app.get(url).document.select(
"div.b-content__inline_items div.b-content__inline_item"
).map {
it.toSearchResult()
}
items.add(HomePageList(fixTitle(header), home))
}
}
if (items.size <= 0) throw ErrorLoadingException()
return HomePageResponse(items)
}
private fun Element.toSearchResult(): SearchResponse {
val title =
this.selectFirst("div.b-content__inline_item-link > a")?.text()?.trim().toString()
val href = this.selectFirst("a")?.attr("href").toString()
val posterUrl = this.select("img").attr("src")
val type = if (this.select("span.info").isNotEmpty()) TvType.TvSeries else TvType.Movie
return if (type == TvType.Movie) {
newMovieSearchResponse(title, href, TvType.Movie) {
this.posterUrl = posterUrl
}
} else {
val episode =
this.select("span.info").text().substringAfter(",").replace(Regex("[^0-9]"), "")
.toIntOrNull()
newAnimeSearchResponse(title, href, TvType.TvSeries) {
this.posterUrl = posterUrl
addDubStatus(
dubExist = true,
dubEpisodes = episode,
subExist = true,
subEpisodes = episode
)
}
}
}
override suspend fun search(query: String): List<SearchResponse> {
val link = "$mainUrl/search/?do=search&subaction=search&q=$query"
val document = app.get(link).document
return document.select("div.b-content__inline_items div.b-content__inline_item").map {
it.toSearchResult()
}
}
override suspend fun load(url: String): LoadResponse {
val document = app.get(url).document
val id = url.split("/").last().split("-").first()
val title = (document.selectFirst("div.b-post__origtitle")?.text()?.trim()
?: document.selectFirst("div.b-post__title h1")?.text()?.trim()).toString()
val poster = fixUrlNull(document.selectFirst("div.b-sidecover img")?.attr("src"))
val tags =
document.select("table.b-post__info > tbody > tr:nth-child(5) span[itemprop=genre]")
.map { it.text() }
val year = document.select("div.film-info > div:nth-child(2) a").text().toIntOrNull()
val tvType = if (document.select("div#simple-episodes-tabs")
.isNullOrEmpty()
) TvType.Movie else TvType.TvSeries
val description = document.selectFirst("div.b-post__description_text")?.text()?.trim()
val trailer = app.post(
"$mainUrl/engine/ajax/gettrailervideo.php",
data = mapOf("id" to id),
referer = url
).parsedSafe<Trailer>()?.code.let {
Jsoup.parse(it.toString()).select("iframe").attr("src")
}
val rating =
document.selectFirst("table.b-post__info > tbody > tr:nth-child(1) span.bold")?.text()
.toRatingInt()
val actors = document.select("table.b-post__info > tbody > tr:last-child span.item").map {
Actor(
it.selectFirst("span[itemprop=name]")?.text().toString(),
it.selectFirst("span[itemprop=actor]")?.attr("data-photo")
)
}
val recommendations = document.select("div.b-sidelist div.b-content__inline_item").map {
it.toSearchResult()
}
val data = HashMap<String, Any>()
val server = ArrayList<Map<String, String>>()
data["id"] = id
data["favs"] = document.selectFirst("input#ctrl_favs")?.attr("value").toString()
data["ref"] = url
return if (tvType == TvType.TvSeries) {
document.select("ul#translators-list li").map { res ->
server.add(
mapOf(
"translator_name" to res.text(),
"translator_id" to res.attr("data-translator_id"),
)
)
}
val episodes = document.select("div#simple-episodes-tabs ul li").map {
val season = it.attr("data-season_id").toIntOrNull()
val episode = it.attr("data-episode_id").toIntOrNull()
val name = "Episode $episode"
data["season"] = "$season"
data["episode"] = "$episode"
data["server"] = server
data["action"] = "get_stream"
Episode(
data.toJson(),
name,
season,
episode,
)
}
newTvSeriesLoadResponse(title, url, TvType.TvSeries, episodes) {
this.posterUrl = poster
this.year = year
this.plot = description
this.tags = tags
this.rating = rating
addActors(actors)
this.recommendations = recommendations
addTrailer(trailer)
}
} else {
document.select("ul#translators-list li").map { res ->
server.add(
mapOf(
"translator_name" to res.text(),
"translator_id" to res.attr("data-translator_id"),
"camrip" to res.attr("data-camrip"),
"ads" to res.attr("data-ads"),
"director" to res.attr("data-director")
)
)
}
data["server"] = server
data["action"] = "get_movie"
newMovieLoadResponse(title, url, TvType.Movie, data.toJson()) {
this.posterUrl = poster
this.year = year
this.plot = description
this.tags = tags
this.rating = rating
addActors(actors)
this.recommendations = recommendations
addTrailer(trailer)
}
}
}
private fun decryptStreamUrl(data: String): String {
fun getTrash(arr: List<String>, item: Int): List<String> {
val trash = ArrayList<List<String>>()
for (i in 1..item) {
trash.add(arr)
}
return trash.reduce { acc, list ->
val temp = ArrayList<String>()
acc.forEach { ac ->
list.forEach { li ->
temp.add(ac.plus(li))
}
}
return@reduce temp
}
}
val trashList = listOf("@", "#", "!", "^", "$")
val trashSet = getTrash(trashList, 2) + getTrash(trashList, 3)
var trashString = data.replace("#h", "").split("//_//").joinToString("")
trashSet.forEach {
val temp = base64Encode(it.toByteArray())
trashString = trashString.replace(temp, "")
}
return base64Decode(trashString)
}
private fun cleanCallback(
source: String,
url: String,
quality: String,
isM3u8: Boolean,
sourceCallback: (ExtractorLink) -> Unit
) {
sourceCallback.invoke(
ExtractorLink(
source,
source,
url,
"$mainUrl/",
getQuality(quality),
isM3u8,
headers = mapOf(
"Origin" to mainUrl
)
)
)
}
private fun getLanguage(str: String): String {
return when (str) {
"Русский" -> "Russian"
"Українська" -> "Ukrainian"
else -> str
}
}
private fun getQuality(str: String): Int {
return when (str) {
"360p" -> Qualities.P240.value
"480p" -> Qualities.P360.value
"720p" -> Qualities.P480.value
"1080p" -> Qualities.P720.value
"1080p Ultra" -> Qualities.P1080.value
else -> getQualityFromName(str)
}
}
private fun invokeSources(
source: String,
url: String,
subtitle: String,
subCallback: (SubtitleFile) -> Unit,
sourceCallback: (ExtractorLink) -> Unit
) {
decryptStreamUrl(url).split(",").map { links ->
val quality =
Regex("\\[([0-9]*p.*?)]").find(links)?.groupValues?.getOrNull(1)
.toString().trim()
links.replace("[$quality]", "").split("or").map { it.trim() }
.map { link ->
if (link.endsWith(".m3u8")) {
cleanCallback(
"$source (Main)",
link,
quality,
true,
sourceCallback,
)
} else {
cleanCallback(
"$source (Backup)",
link,
quality,
false,
sourceCallback,
)
}
}
}
subtitle.split(",").map { sub ->
val language =
Regex("\\[(.*)]").find(sub)?.groupValues?.getOrNull(1)
.toString()
val link = sub.replace("[$language]", "").trim()
subCallback.invoke(
SubtitleFile(
getLanguage(language),
link
)
)
}
}
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
tryParseJson<Data>(data)?.let { res ->
if (res.server?.isEmpty() == true) {
val document = app.get(res.ref ?: return@let).document
document.select("script").map { script ->
if (script.data().contains("sof.tv.initCDNMoviesEvents(")) {
val dataJson =
script.data().substringAfter("false, {").substringBefore("});")
tryParseJson<LocalSources>("{$dataJson}")?.let { source ->
invokeSources(
this.name,
source.streams,
source.subtitle.toString(),
subtitleCallback,
callback
)
}
}
}
} else {
res.server?.apmap { server ->
suspendSafeApiCall {
app.post(
url = "$mainUrl/ajax/get_cdn_series/?t=${Date().time}",
data = mapOf(
"id" to res.id,
"translator_id" to server.translator_id,
"favs" to res.favs,
"is_camrip" to server.camrip,
"is_ads" to server.ads,
"is_director" to server.director,
"season" to res.season,
"episode" to res.episode,
"action" to res.action,
).filterValues { it != null }.mapValues { it.value as String },
referer = res.ref
).parsedSafe<Sources>()?.let { source ->
invokeSources(
server.translator_name.toString(),
source.url,
source.subtitle.toString(),
subtitleCallback,
callback
)
}
}
}
}
}
return true
}
data class LocalSources(
@JsonProperty("streams") val streams: String,
@JsonProperty("subtitle") val subtitle: Any?,
)
data class Sources(
@JsonProperty("url") val url: String,
@JsonProperty("subtitle") val subtitle: Any?,
)
data class Server(
@JsonProperty("translator_name") val translator_name: String?,
@JsonProperty("translator_id") val translator_id: String?,
@JsonProperty("camrip") val camrip: String?,
@JsonProperty("ads") val ads: String?,
@JsonProperty("director") val director: String?,
)
data class Data(
@JsonProperty("id") val id: String?,
@JsonProperty("favs") val favs: String?,
@JsonProperty("server") val server: List<Server>?,
@JsonProperty("season") val season: String?,
@JsonProperty("episode") val episode: String?,
@JsonProperty("action") val action: String?,
@JsonProperty("ref") val ref: String?,
)
data class Trailer(
@JsonProperty("success") val success: Boolean?,
@JsonProperty("code") val code: String?,
)
}

View file

@ -12,7 +12,7 @@ import java.net.URI
import java.util.ArrayList
class IdlixProvider : MainAPI() {
override var mainUrl = "https://idlix.xn--6frz82g"
override var mainUrl = "https://94.103.82.88"
override var name = "Idlix"
override val hasMainPage = true
override var lang = "id"

View file

@ -0,0 +1,217 @@
package com.lagradost.cloudstream3.movieproviders
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.LoadResponse.Companion.addActors
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
import com.lagradost.cloudstream3.mvvm.safeApiCall
import com.lagradost.cloudstream3.utils.*
import org.jsoup.nodes.Element
import java.net.URLDecoder
import java.util.ArrayList
class PhimmoichillProvider : MainAPI() {
override var mainUrl = "https://phimmoichill.net"
override var name = "Phimmoichill"
override val hasMainPage = true
override var lang = "vi"
override val hasDownloadSupport = true
override val supportedTypes = setOf(
TvType.Movie,
TvType.TvSeries,
TvType.Anime,
TvType.AsianDrama
)
override suspend fun getMainPage(): HomePageResponse {
val document = app.get(mainUrl).document
val homePageList = ArrayList<HomePageList>()
document.select("div.container div.block").forEach { block ->
val header = fixTitle(block.selectFirst("h2")!!.text())
val items = block.select("li.item").mapNotNull {
it.toSearchResult()
}
if (items.isNotEmpty()) homePageList.add(HomePageList(header, items))
}
return HomePageResponse(homePageList)
}
private fun decode(input: String): String? = URLDecoder.decode(input, "utf-8")
private fun Element.toSearchResult(): SearchResponse {
val title = this.selectFirst("p,h3")?.text()?.trim().toString()
val href = fixUrl(this.selectFirst("a")!!.attr("href"))
val posterUrl = decode(this.selectFirst("img")!!.attr("src").substringAfter("url="))
val temp = this.select("span.label").text()
return if (temp.contains(Regex("\\d"))) {
val episode = Regex("(\\((\\d+))|(\\s(\\d+))").find(temp)?.groupValues?.map { num ->
num.replace(Regex("\\(|\\s"), "")
}?.distinct()?.firstOrNull()?.toIntOrNull()
newAnimeSearchResponse(title, href, TvType.TvSeries) {
this.posterUrl = posterUrl
addSub(episode)
}
} else {
val quality =
temp.replace(Regex("(-.*)|(\\|.*)|(?i)(VietSub.*)|(?i)(Thuyết.*)"), "").trim()
newMovieSearchResponse(title, href, TvType.Movie) {
this.posterUrl = posterUrl
addQuality(quality)
}
}
}
override suspend fun search(query: String): List<SearchResponse> {
val link = "$mainUrl/tim-kiem/$query"
val document = app.get(link).document
return document.select("ul.list-film li").map {
it.toSearchResult()
}
}
override suspend fun load(url: String): LoadResponse {
val document = app.get(url).document
val title = document.selectFirst("h1[itemprop=name]")?.text()?.trim().toString()
val link = document.select("ul.list-button li:last-child a").attr("href")
val poster = document.selectFirst("div.image img[itemprop=image]")?.attr("src")
val tags = document.select("ul.entry-meta.block-film li:nth-child(4) a").map { it.text() }
val year = document.select("ul.entry-meta.block-film li:nth-child(2) a").text().trim()
.toIntOrNull()
val tvType = if (document.select("div.latest-episode").isNotEmpty()
) TvType.TvSeries else TvType.Movie
val description = document.select("div#film-content-wrapper").text().trim()
val trailer =
document.select("div#trailer script").last()?.data()?.substringAfter("file: \"")
?.substringBefore("\",")
val rating =
document.select("ul.entry-meta.block-film li:nth-child(7) span").text().toRatingInt()
val actors = document.select("ul.entry-meta.block-film li:last-child a").map { it.text() }
val recommendations = document.select("ul#list-film-realted li.item").map {
it.toSearchResult()
}
return if (tvType == TvType.TvSeries) {
val docEpisodes = app.get(link).document
val episodes = docEpisodes.select("ul#list_episodes > li").map {
val href = it.select("a").attr("href")
val episode =
it.select("a").text().replace(Regex("[^0-9]"), "").trim().toIntOrNull()
val name = "Episode $episode"
Episode(
data = href,
name = name,
episode = episode,
)
}
newTvSeriesLoadResponse(title, url, TvType.TvSeries, episodes) {
this.posterUrl = poster
this.year = year
this.plot = description
this.tags = tags
this.rating = rating
addActors(actors)
this.recommendations = recommendations
addTrailer(trailer)
}
} else {
newMovieLoadResponse(title, url, TvType.Movie, link) {
this.posterUrl = poster
this.year = year
this.plot = description
this.tags = tags
this.rating = rating
addActors(actors)
this.recommendations = recommendations
addTrailer(trailer)
}
}
}
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
val document = app.get(data).document
val key = document.select("div#content script").mapNotNull { script ->
if (script.data().contains("filmInfo.episodeID =")) {
val id = script.data().substringAfter("filmInfo.episodeID = parseInt('")
.substringBefore("');")
app.post(
url = "$mainUrl/pmplayer.php",
data = mapOf("qcao" to id),
referer = data,
headers = mapOf("X-Requested-With" to "XMLHttpRequest")
).text.substringAfterLast("iniPlayers(\"").substringBefore("\",")
} else {
null
}
}.first()
listOf(
Pair("https://so-trym.topphimmoi.org/hlspm/$key", "PMFAST"),
Pair("https://dash.megacdn.xyz/hlspm/$key", "PMHLS"),
Pair("https://dash.megacdn.xyz/dast/$key/index.m3u8", "PMBK")
).apmap { (link, source) ->
safeApiCall {
if (source == "PMBK") {
callback.invoke(
ExtractorLink(
source,
source,
link,
referer = "$mainUrl/",
quality = Qualities.P1080.value,
isM3u8 = true
)
)
} else {
val playList = app.get(link, referer = "$mainUrl/")
.parsedSafe<ResponseM3u>()?.main?.segments?.map { segment ->
PlayListItem(
segment.link,
(segment.du.toFloat() * 1_000_000).toLong()
)
}
callback.invoke(
ExtractorLinkPlayList(
source,
source,
playList ?: return@safeApiCall,
referer = "$mainUrl/",
quality = Qualities.P1080.value,
headers = mapOf(
// "If-None-Match" to "*",
"Origin" to mainUrl,
)
)
)
}
}
}
return true
}
data class Segment(
@JsonProperty("du") val du: String,
@JsonProperty("link") val link: String,
)
data class DataM3u(
@JsonProperty("segments") val segments: List<Segment>?,
)
data class ResponseM3u(
@JsonProperty("2048p") val main: DataM3u?,
)
}

View file

@ -81,7 +81,7 @@ class RebahinProvider : MainAPI() {
this.select("div.mli-eps > span").text().replace(Regex("[^0-9]"), "").toIntOrNull()
newAnimeSearchResponse(title, href, TvType.TvSeries) {
this.posterUrl = posterUrl
addDubStatus(dubExist = false, subExist = true, subEpisodes = episode)
addSub(episode)
}
}
}
@ -114,19 +114,18 @@ class RebahinProvider : MainAPI() {
.ownText().replace(Regex("[^0-9]"), "").toIntOrNull()
val actors = document.select("span[itemprop=actor] > a").map { it.select("span").text() }
val baseLink = fixUrl(document.select("div#mv-info > a").attr("href").toString())
return if (tvType == TvType.TvSeries) {
val baseLink = document.select("div#mv-info > a").attr("href")
val episodes = app.get(baseLink).document.select("div#list-eps > a").map {
val name = it.text().replace(Regex("Server\\s?\\d"), "").trim()
name
}.distinct().map {
val name = it
val epNum = it.replace(Regex("[^0-9]"), "").toIntOrNull()
val link = "$baseLink?ep=$epNum"
newEpisode(link) {
this.name = name
this.episode = epNum
}
Pair(it.text(), it.attr("data-iframe"))
}.groupBy { it.first }.map { eps ->
Episode(
data = eps.value.map { fixUrl(base64Decode(it.second)) }.toString(),
name = eps.key,
episode = eps.key.filter { it.isDigit() }.toIntOrNull()
)
}
newTvSeriesLoadResponse(title, url, TvType.TvSeries, episodes) {
this.posterUrl = poster
@ -139,8 +138,12 @@ class RebahinProvider : MainAPI() {
addTrailer(trailer)
}
} else {
val episodes = document.select("div#mv-info > a").attr("href")
newMovieLoadResponse(title, url, TvType.Movie, episodes) {
val links =
app.get(baseLink).document.select("div#server-list div.server-wrapper div[id*=episode]")
.map {
fixUrl(base64Decode(it.attr("data-iframe")))
}.toString()
newMovieLoadResponse(title, url, TvType.Movie, links) {
this.posterUrl = poster
this.year = year
this.plot = description
@ -153,18 +156,6 @@ class RebahinProvider : MainAPI() {
}
}
private data class ResponseLocal(
@JsonProperty("file") val file: String,
@JsonProperty("label") val label: String,
@JsonProperty("type") val type: String?
)
private data class Tracks(
@JsonProperty("file") val file: String,
@JsonProperty("label") val label: String?,
@JsonProperty("kind") val kind: String?
)
private suspend fun invokeLokalSource(
url: String,
name: String,
@ -182,7 +173,8 @@ class RebahinProvider : MainAPI() {
document.select("script").map { script ->
if (script.data().contains("sources: [")) {
val source = tryParseJson<ResponseLocal>(
script.data().substringAfter("sources: [").substringBefore("],"))
script.data().substringAfter("sources: [").substringBefore("],")
)
val m3uData = app.get(source!!.file, referer = ref).text
val quality = Regex("\\d{3,4}\\.m3u8").findAll(m3uData).map { it.value }.toList()
@ -213,28 +205,6 @@ class RebahinProvider : MainAPI() {
}
}
private data class Captions(
@JsonProperty("id") val id: String,
@JsonProperty("hash") val hash: String,
@JsonProperty("language") val language: String,
)
private data class Data(
@JsonProperty("file") val file: String,
@JsonProperty("label") val label: String,
)
private data class Player(
@JsonProperty("poster_file") val poster_file: String,
)
private data class ResponseKotakAjair(
@JsonProperty("success") val success: Boolean,
@JsonProperty("player") val player: Player,
@JsonProperty("data") val data: List<Data>?,
@JsonProperty("captions") val captions: List<Captions>?
)
private suspend fun invokeKotakAjairSource(
url: String,
subCallback: (SubtitleFile) -> Unit,
@ -277,45 +247,26 @@ class RebahinProvider : MainAPI() {
callback: (ExtractorLink) -> Unit
): Boolean {
val sources = if (data.contains("/play")) {
app.get(data).document.select(".server-wrapper").mapNotNull {
val iframeLink =
"${mainUrl}/iembed/?source=${it.selectFirst("div.server")?.attr("data-iframe")}"
app.get(iframeLink).document.select("iframe").attr("src")
}
} else {
val fixData = Regex("(.*?)\\?ep").find(data)?.groupValues?.get(1).toString()
val document = app.get(fixData).document
val ep = Regex("\\?ep=([0-9]+)").find(data)?.groupValues?.get(1).toString()
val title = document.selectFirst("div#list-eps > a")?.text()?.replace(Regex("[\\d]"), "")
?.trim()?.replace("Server", "")?.trim()
document.select("div#list-eps > a:matches(${title}\\s?${ep}$)").mapNotNull {
val iframeLink = "${mainUrl}/iembed/?source=${it.attr("data-iframe")}"
app.get(iframeLink).document.select("iframe")
.attr("src")
}
}
sources.apmap {
data.removeSurrounding("[", "]").split(",").map { it.trim() }.apmap { link ->
safeApiCall {
when {
it.startsWith("http://172.96.161.72") -> invokeLokalSource(
it,
link.startsWith("http://172.96.161.72") -> invokeLokalSource(
link,
this.name,
"http://172.96.161.72/",
subtitleCallback,
callback
)
it.startsWith("https://kotakajair.xyz") -> invokeKotakAjairSource(
it,
link.startsWith("https://kotakajair.xyz") -> invokeKotakAjairSource(
link,
subtitleCallback,
callback
)
else -> {
loadExtractor(it, data, callback)
if (it.startsWith("https://sbfull.com")) {
loadExtractor(link, "$mainUrl/", callback)
if (link.startsWith("https://sbfull.com")) {
val response = app.get(
it, interceptor = WebViewResolver(
link, interceptor = WebViewResolver(
Regex("""\.srt""")
)
)
@ -334,5 +285,39 @@ class RebahinProvider : MainAPI() {
return true
}
private data class ResponseLocal(
@JsonProperty("file") val file: String,
@JsonProperty("label") val label: String,
@JsonProperty("type") val type: String?
)
private data class Tracks(
@JsonProperty("file") val file: String,
@JsonProperty("label") val label: String?,
@JsonProperty("kind") val kind: String?
)
private data class Captions(
@JsonProperty("id") val id: String,
@JsonProperty("hash") val hash: String,
@JsonProperty("language") val language: String,
)
private data class Data(
@JsonProperty("file") val file: String,
@JsonProperty("label") val label: String,
)
private data class Player(
@JsonProperty("poster_file") val poster_file: String,
)
private data class ResponseKotakAjair(
@JsonProperty("success") val success: Boolean,
@JsonProperty("player") val player: Player,
@JsonProperty("data") val data: List<Data>?,
@JsonProperty("captions") val captions: List<Captions>?
)
}

View file

@ -0,0 +1,175 @@
package com.lagradost.cloudstream3.movieproviders
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.LoadResponse.Companion.addActors
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
import com.lagradost.cloudstream3.mvvm.safeApiCall
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.M3u8Helper
import org.jsoup.Jsoup
import org.jsoup.nodes.Element
import java.util.*
class UakinoProvider : MainAPI() {
override var mainUrl = "https://uakino.club"
override var name = "Uakino"
override val hasMainPage = true
override var lang = "uk"
override val hasDownloadSupport = true
override val supportedTypes = setOf(
TvType.Movie,
TvType.TvSeries,
TvType.Anime
)
override suspend fun getMainPage(): HomePageResponse {
val document = app.get(mainUrl).document
val homePageList = ArrayList<HomePageList>()
document.select("div.main-section-inner").forEach { block ->
val header = block.selectFirst("p.sidebar-title")?.text()?.trim().toString()
val items = block.select("div.owl-item, div.movie-item").map {
it.toSearchResponse()
}
if (items.isNotEmpty()) homePageList.add(HomePageList(header, items))
}
return HomePageResponse(homePageList)
}
private fun Element.toSearchResponse(): SearchResponse {
val title = this.selectFirst("a.movie-title")?.text()?.trim().toString()
val href = this.selectFirst("a.movie-title")?.attr("href").toString()
val posterUrl = fixUrlNull(this.selectFirst("img")?.attr("src"))
return newMovieSearchResponse(title, href, TvType.Movie) {
this.posterUrl = posterUrl
}
}
override suspend fun search(query: String): List<SearchResponse> {
val document = app.post(
url = mainUrl,
data = mapOf(
"do" to "search",
"subaction" to "search",
"story" to query.replace(" ", "+")
)
).document
return document.select("div.movie-item.short-item").map {
it.toSearchResponse()
}
}
override suspend fun load(url: String): LoadResponse {
val document = app.get(url).document
val title = document.selectFirst("h1 span.solototle")?.text()?.trim().toString()
val poster = fixUrl(document.selectFirst("div.film-poster img")?.attr("src").toString())
val tags = document.select("div.film-info > div:nth-child(4) a").map { it.text() }
val year = document.select("div.film-info > div:nth-child(2) a").text().toIntOrNull()
val tvType = if (url.contains(Regex("(/anime-series)|(/seriesss)|(/cartoonseries)"))) TvType.TvSeries else TvType.Movie
val description = document.selectFirst("div[itemprop=description]")?.text()?.trim()
val trailer = document.selectFirst("iframe#pre")?.attr("data-src")
val rating = document.selectFirst("div.film-info > div:nth-child(8) div.fi-desc")?.text()
?.substringBefore("/").toRatingInt()
val actors = document.select("div.film-info > div:nth-child(6) a").map { it.text() }
val recommendations = document.select("div#full-slides div.owl-item").map {
it.toSearchResponse()
}
return if (tvType == TvType.TvSeries) {
val id = url.split("/").last().split("-").first()
val episodes = app.get("$mainUrl/engine/ajax/playlists.php?news_id=$id&xfield=playlist&time=${Date().time}")
.parsedSafe<Responses>()?.response.let {
Jsoup.parse(it.toString()).select("ul > li").mapNotNull { eps ->
val href = fixUrl(eps.attr("data-file"))
val name = eps.text().trim()
if (href.isNotEmpty()) {
Episode(
href,
name,
)
} else {
null
}
}
}
newTvSeriesLoadResponse(title, url, TvType.TvSeries, episodes) {
this.posterUrl = poster
this.year = year
this.plot = description
this.tags = tags
this.rating = rating
addActors(actors)
this.recommendations = recommendations
addTrailer(trailer)
}
} else {
newMovieLoadResponse(title, url, TvType.Movie, url) {
this.posterUrl = poster
this.year = year
this.plot = description
this.tags = tags
this.rating = rating
addActors(actors)
this.recommendations = recommendations
addTrailer(trailer)
}
}
}
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
val links = ArrayList<String>()
if (data.startsWith("https://ashdi.vip")) {
links.add(data)
} else {
val iframeUrl = app.get(data).document.selectFirst("iframe#pre")?.attr("src")
if (iframeUrl.isNullOrEmpty()) {
val id = data.split("/").last().split("-").first()
app.get("$mainUrl/engine/ajax/playlists.php?news_id=$id&xfield=playlist&time=${Date().time}")
.parsedSafe<Responses>()?.response.let {
Jsoup.parse(it.toString()).select("ul > li").mapNotNull { mirror ->
links.add(fixUrl(mirror.attr("data-file")))
}
}
} else {
links.add(iframeUrl)
}
}
links.apmap { link ->
safeApiCall {
app.get(link, referer = "$mainUrl/").document.select("script").map { script ->
if (script.data().contains("var player = new Playerjs({")) {
val m3uLink =
script.data().substringAfterLast("file:\"").substringBefore("\",")
M3u8Helper.generateM3u8(
source = this.name,
streamUrl = m3uLink,
referer = "https://ashdi.vip/"
).forEach(callback)
}
}
}
}
return true
}
data class Responses(
@JsonProperty("success") val success: Boolean?,
@JsonProperty("response") val response: String,
)
}

View file

@ -11,8 +11,7 @@ abstract class AccountManager(private val defIndex: Int) : AuthAPI {
val malApi = MALApi(0)
val aniListApi = AniListApi(0)
val openSubtitlesApi = OpenSubtitlesApi(0)
// Removed because of cloudflare
// val indexSubtitlesApi = IndexSubtitleApi()
val indexSubtitlesApi = IndexSubtitleApi()
val nginxApi = NginxApi(0)
// used to login via app intent
@ -39,8 +38,7 @@ abstract class AccountManager(private val defIndex: Int) : AuthAPI {
val subtitleProviders
get() = listOf(
openSubtitlesApi,
// Removed because of cloudflare
// indexSubtitlesApi
indexSubtitlesApi
)
const val appString = "cloudstreamapp"

View file

@ -282,6 +282,8 @@ val extractorApis: Array<ExtractorApi> = arrayOf(
Filesim(),
Linkbox(),
YoutubeExtractor(),
YoutubeShortLinkExtractor(),
)