mirror of
https://github.com/recloudstream/cloudstream.git
synced 2024-08-15 01:53:11 +00:00
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:
parent
524d0717e2
commit
b970ca0668
12 changed files with 978 additions and 142 deletions
|
@ -101,6 +101,10 @@ object APIHolder {
|
|||
IdlixProvider(),
|
||||
MultiplexProvider(),
|
||||
VidSrcProvider(),
|
||||
UakinoProvider(),
|
||||
PhimmoichillProvider(),
|
||||
HDrezkaProvider(),
|
||||
|
||||
|
||||
// Metadata providers
|
||||
//TmdbProvider(),
|
||||
|
|
|
@ -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,19 +176,21 @@ 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 {
|
||||
val url = it.attr("src")
|
||||
val quality = it.attr("size").toInt()
|
||||
callback.invoke(
|
||||
ExtractorLink(
|
||||
name,
|
||||
name,
|
||||
url,
|
||||
referer = "$mainUrl/",
|
||||
quality = quality
|
||||
suspendSafeApiCall {
|
||||
val url = it.attr("src")
|
||||
val quality = it.attr("size").toInt()
|
||||
callback.invoke(
|
||||
ExtractorLink(
|
||||
name,
|
||||
name,
|
||||
url,
|
||||
referer = "$mainUrl/",
|
||||
quality = quality
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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?,
|
||||
)
|
||||
|
||||
}
|
|
@ -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?,
|
||||
)
|
||||
|
||||
}
|
|
@ -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"
|
||||
|
|
|
@ -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?,
|
||||
)
|
||||
|
||||
}
|
|
@ -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>?
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
|
||||
}
|
|
@ -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"
|
||||
|
|
|
@ -282,6 +282,8 @@ val extractorApis: Array<ExtractorApi> = arrayOf(
|
|||
|
||||
Filesim(),
|
||||
|
||||
Linkbox(),
|
||||
|
||||
YoutubeExtractor(),
|
||||
YoutubeShortLinkExtractor(),
|
||||
)
|
||||
|
|
Loading…
Reference in a new issue