NEW!! Providers (Indonesian) (#1151)

* 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

Co-authored-by: Osten <11805592+LagradOst@users.noreply.github.com>
This commit is contained in:
Hexated 2022-07-07 01:15:48 +07:00 committed by GitHub
parent 40b66d48ca
commit ed8164ca40
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 1532 additions and 107 deletions

View File

@ -97,7 +97,9 @@ object APIHolder {
RebahinProvider(),
LayarKacaProvider(),
HDTodayProvider(),
OpenVidsProvider(),
OpenVidsProvider(),
IdlixProvider(),
MultiplexProvider(),
// Metadata providers
//TmdbProvider(),
@ -132,6 +134,8 @@ object APIHolder {
GomunimeProvider(),
NontonAnimeIDProvider(),
KuronimeProvider(),
OtakudesuProvider(),
AnimeIndoProvider(),
//MultiAnimeProvider(),
NginxProvider(),
OlgplyProvider(),
@ -543,6 +547,11 @@ fun capitalizeStringNullable(str: String?): String? {
}
}
fun fixTitle(str: String): String {
return str.split(" ").joinToString(" ") { it.lowercase()
.replaceFirstChar { char -> if (char.isLowerCase()) char.titlecase(Locale.getDefault()) else it } }
}
/** https://www.imdb.com/title/tt2861424/ -> tt2861424 */
fun imdbUrlToId(url: String): String? {
return Regex("/title/(tt[0-9]*)").find(url)?.groupValues?.get(1)

View File

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

View File

@ -2,6 +2,7 @@ package com.lagradost.cloudstream3.animeproviders
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.mvvm.safeApiCall
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
@ -14,7 +15,6 @@ import org.jsoup.Jsoup
class GomunimeProvider : MainAPI() {
override var mainUrl = "https://185.231.223.76"
override var name = "Gomunime"
override val hasQuickSearch = false
override val hasMainPage = true
override var lang = "id"
override val hasDownloadSupport = true
@ -75,7 +75,7 @@ class GomunimeProvider : MainAPI() {
.toIntOrNull()
newAnimeSearchResponse(title, href, type) {
this.posterUrl = posterUrl
addDubStatus(dubExist = false, subExist = true, subEpisodes = epNum)
addSub(epNum)
}
}
items.add(HomePageList(name, home))
@ -139,7 +139,7 @@ class GomunimeProvider : MainAPI() {
)?.groupValues?.get(1)?.toIntOrNull()
val status = getStatus(document.selectFirst(".spe > span")!!.ownText())
val description = document.select("div[itemprop = description] > p").text()
val trailer = document.selectFirst("div.embed-responsive noscript iframe")?.attr("src")
val episodes = parseJson<List<EpisodeElement>>(
Regex("var episodelist = (\\[.*])").find(
document.select(".bixbox.bxcl.epcheck > script").toString().trim()
@ -158,6 +158,7 @@ class GomunimeProvider : MainAPI() {
showStatus = status
plot = description
this.tags = tags
addTrailer(trailer)
}
}

View File

@ -10,7 +10,7 @@ import org.jsoup.Jsoup
import org.jsoup.nodes.Element
class KuronimeProvider : MainAPI() {
override var mainUrl = "https://185.231.223.254"
override var mainUrl = "https://45.12.2.2"
override var name = "Kuronime"
override val hasQuickSearch = false
override val hasMainPage = true
@ -46,7 +46,7 @@ class KuronimeProvider : MainAPI() {
document.select(".bixbox").forEach { block ->
val header = block.select(".releases > h3").text().trim()
val animes = block.select("article").mapNotNull {
val animes = block.select("article").map {
it.toSearchResult()
}
if (animes.isNotEmpty()) homePageList.add(HomePageList(header, animes))
@ -74,15 +74,15 @@ class KuronimeProvider : MainAPI() {
}
}
private fun Element.toSearchResult(): SearchResponse {
val href = getProperAnimeLink(fixUrl(this.select("a").attr("href")))
private fun Element.toSearchResult(): AnimeSearchResponse {
val href = getProperAnimeLink(fixUrlNull(this.selectFirst("a")?.attr("href")).toString())
val title = this.select(".bsuxtt, .tt > h4").text().trim()
val posterUrl = fixUrl(this.select("img").attr("src"))
val posterUrl = fixUrlNull(this.selectFirst("div.view,div.bt")?.nextElementSibling()?.select("img")?.attr("data-src"))
val epNum = this.select(".ep").text().replace(Regex("[^0-9]"), "").trim().toIntOrNull()
return newAnimeSearchResponse(title, href, TvType.Anime) {
val tvType = getType(this.selectFirst(".bt > span")?.text().toString())
return newAnimeSearchResponse(title, href, tvType) {
this.posterUrl = posterUrl
addDubStatus(dubExist = false, subExist = true, subEpisodes = epNum)
addSub(epNum)
}
}
@ -91,16 +91,8 @@ class KuronimeProvider : MainAPI() {
val link = "$mainUrl/?s=$query"
val document = app.get(link).document
return document.select("article.bs").mapNotNull {
val title = it.selectFirst(".tt > h4")!!.text().trim()
val poster = it.select("img").attr("src")
val tvType = getType(it.selectFirst(".bt > span")?.text().toString())
val href = getProperAnimeLink(fixUrl(it.selectFirst("a")!!.attr("href")))
newAnimeSearchResponse(title, href, tvType) {
this.posterUrl = poster
addDubStatus(dubExist = false, subExist = true)
}
return document.select("article.bs").map {
it.toSearchResult()
}
}
@ -108,14 +100,12 @@ class KuronimeProvider : MainAPI() {
val document = app.get(url).document
val title = document.selectFirst(".entry-title")?.text().toString().trim()
val poster = document.select("div[itemprop=image]").joinToString {
it.select("img").attr("src")
}
val poster = document.selectFirst("div.l[itemprop=image] > img")?.attr("data-src")
val tags = document.select(".infodetail > ul > li:nth-child(2) > a").map { it.text() }
val type = getType(
document.selectFirst(".infodetail > ul > li:nth-child(7)")?.ownText()?.trim().toString()
)
val trailer = document.select("iframe.entered.lazyloaded").attr("src")
val trailer = document.selectFirst("div.tply iframe")?.attr("data-lazy-src")
val year = Regex("\\d, ([0-9]*)").find(
document.select(".infodetail > ul > li:nth-child(5)").text()
)?.groupValues?.get(1)?.toIntOrNull()
@ -126,9 +116,10 @@ class KuronimeProvider : MainAPI() {
val description = document.select("span.const > p").text()
val episodes = document.select("div.bixbox.bxcl > ul > li").map {
val name = it.selectFirst("a")?.text()?.trim()?.replace("Episode", title)
val name = it.selectFirst("a")?.text()?.trim()
val episode = it.selectFirst("a")?.text()?.trim()?.replace("Episode", "")?.trim()?.toIntOrNull()
val link = it.selectFirst("a")!!.attr("href")
Episode(link, name)
Episode(link, name = name, episode = episode)
}.reversed()
return newAnimeLoadResponse(title, url, type) {
@ -147,7 +138,7 @@ class KuronimeProvider : MainAPI() {
url: String,
sourceCallback: (ExtractorLink) -> Unit
) {
val doc = app.get(url).document
val doc = app.get(url, referer = "${mainUrl}/").document
doc.select("script").map { script ->
if (script.data().contains("function jalankan_jwp() {")) {

View File

@ -1,6 +1,7 @@
package com.lagradost.cloudstream3.animeproviders
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.loadExtractor
import org.jsoup.nodes.Element
@ -43,10 +44,10 @@ class NeonimeProvider : MainAPI() {
val document = app.get(mainUrl).document
val homePageList = ArrayList<HomePageList>()
document.select("div.item_1.items").forEach { block ->
val header = block.previousElementSibling()?.select("h1")!!.text()
val animes = block.select("div.item").mapNotNull {
document.select("div.item_1.items,div#slid01").forEach { block ->
val header = block.previousElementSibling()?.select("h1")?.text() ?: block.selectFirst("h3")?.text().toString()
val animes = block.select("div.item").map {
it.toSearchResult()
}
if (animes.isNotEmpty()) homePageList.add(HomePageList(header, animes))
@ -58,26 +59,36 @@ class NeonimeProvider : MainAPI() {
private fun getProperAnimeLink(uri: String): String {
return when {
uri.contains("/episode") -> {
val href = "$mainUrl/tvshows/" + Regex("episode/(.*)-\\d{1,2}x\\d+").find(uri)?.groupValues?.get(1).toString()
when {
!href.contains("-subtitle-indonesia") -> "$href-subtitle-indonesia"
href.contains("-special") -> href.replace(Regex("-x\\d+"), "")
else -> href
val title = uri.substringAfter("$mainUrl/episode/").let { tt ->
val fixTitle = Regex("(.*)-\\d{1,2}x\\d+").find(tt)?.groupValues?.getOrNull(1).toString()
when {
!tt.contains("-season") && !tt.contains(Regex("-1x\\d+")) && !tt.contains("one-piece") -> "$fixTitle-season-${Regex("-(\\d{1,2})x\\d+").find(tt)?.groupValues?.getOrNull(1).toString()}"
tt.contains("-special") -> fixTitle.replace(Regex("-x\\d+"), "")
!fixTitle.contains("-subtitle-indonesia") -> "$fixTitle-subtitle-indonesia"
else -> fixTitle
}
}
// title = when {
// title.contains("youkoso-jitsuryoku") && !title.contains("-season") -> title.replace("-e-", "-e-tv-")
// else -> title
// }
"$mainUrl/tvshows/$title"
}
else -> uri
}
}
private fun Element.toSearchResult(): SearchResponse {
private fun Element.toSearchResult(): AnimeSearchResponse {
val href = getProperAnimeLink(fixUrl(this.select("a").attr("href")))
val title = this.select("span.tt.title-episode,h2.title-episode-movie").text()
val title = this.select("span.tt.title-episode,h2.title-episode-movie,span.ttps").text()
val posterUrl = fixUrl(this.select("img").attr("data-src"))
val epNum = this.select(".fixyear > h2.text-center").text().replace(Regex("[^0-9]"), "").trim().toIntOrNull()
return newAnimeSearchResponse(title, href, TvType.Anime) {
this.posterUrl = posterUrl
addDubStatus(dubExist = false, subExist = true, subEpisodes = epNum)
addSub(epNum)
}
}
@ -96,7 +107,7 @@ class NeonimeProvider : MainAPI() {
newAnimeSearchResponse(title, href, tvType) {
this.posterUrl = poster
addDubStatus(dubExist = false, subExist = true, subEpisodes = episodes)
addSub(episodes)
}
}
}
@ -112,19 +123,16 @@ class NeonimeProvider : MainAPI() {
val mYear = document.selectFirst("a[href*=release-year]")!!.text().toIntOrNull()
val mDescription = document.select("div[itemprop = description]").text().trim()
val mRating = document.select("span[itemprop = ratingValue]").text().toIntOrNull()
val mTrailer = document.selectFirst("div.youtube_id iframe")?.attr("data-wpfc-original-src")?.substringAfterLast("html#")?.let{ "https://www.youtube.com/embed/$it"}
return MovieLoadResponse(
name = mTitle,
url = url,
this.name,
type = TvType.Movie,
dataUrl = url,
posterUrl = mPoster,
year = mYear,
plot = mDescription,
rating = mRating,
return newMovieLoadResponse(name = mTitle, url = url, type = TvType.Movie, dataUrl = url) {
posterUrl = mPoster
year = mYear
plot = mDescription
rating = mRating
tags = mTags
)
addTrailer(mTrailer)
}
}
else {
val title = document.select("h1[itemprop = name]").text().trim()
@ -133,6 +141,7 @@ class NeonimeProvider : MainAPI() {
val year = document.select("#info a[href*=\"-year/\"]").text().toIntOrNull()
val status = getStatus(document.select("div.metadatac > span").last()!!.text().trim())
val description = document.select("div[itemprop = description] > p").text().trim()
val trailer = document.selectFirst("div.youtube_id_tv iframe")?.attr("data-wpfc-original-src")?.substringAfterLast("html#")?.let{ "https://www.youtube.com/embed/$it"}
val episodes = document.select("ul.episodios > li").mapNotNull {
val name = it.selectFirst(".episodiotitle > a")!!.ownText().trim()
@ -148,6 +157,7 @@ class NeonimeProvider : MainAPI() {
showStatus = status
plot = description
this.tags = tags
addTrailer(trailer)
}
}
}

View File

@ -4,10 +4,8 @@ import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
import com.lagradost.cloudstream3.utils.*
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
import org.jsoup.Jsoup
import org.jsoup.nodes.Element
import java.net.URI
import java.util.ArrayList
class NontonAnimeIDProvider : MainAPI() {
@ -49,15 +47,15 @@ class NontonAnimeIDProvider : MainAPI() {
document.select("section#postbaru").forEach { block ->
val header = block.selectFirst("h2")!!.text().trim()
val animes = block.select("article.animeseries").mapNotNull {
val animes = block.select("article.animeseries").map {
it.toSearchResult()
}
if (animes.isNotEmpty()) homePageList.add(HomePageList(header, animes))
}
document.select("aside#sidebar_right > div.side:nth-child(2)").forEach { block ->
document.select("aside#sidebar_right > div:nth-child(4)").forEach { block ->
val header = block.selectFirst("h3")!!.ownText().trim()
val animes = block.select("li.fullwdth").mapNotNull {
val animes = block.select("li.fullwdth").map {
it.toSearchResultPopular()
}
if (animes.isNotEmpty()) homePageList.add(HomePageList(header, animes))
@ -83,7 +81,7 @@ class NontonAnimeIDProvider : MainAPI() {
}
}
private fun Element.toSearchResult(): SearchResponse {
private fun Element.toSearchResult(): AnimeSearchResponse {
val href = getProperAnimeLink(fixUrl(this.selectFirst("a")!!.attr("href")))
val title = this.selectFirst("h3.title")!!.text()
val posterUrl = fixUrl(this.select("img").attr("data-src"))
@ -95,7 +93,7 @@ class NontonAnimeIDProvider : MainAPI() {
}
private fun Element.toSearchResultPopular(): SearchResponse {
private fun Element.toSearchResultPopular(): AnimeSearchResponse {
val href = getProperAnimeLink(fixUrl(this.selectFirst("a")!!.attr("href")))
val title = this.select("h4").text().trim()
val posterUrl = fixUrl(this.select("img").attr("data-src"))
@ -149,7 +147,7 @@ class NontonAnimeIDProvider : MainAPI() {
val type = getType(document.select("span.typeseries").text().trim())
val rating = document.select("span.nilaiseries").text().trim().toIntOrNull()
val description = document.select(".entry-content.seriesdesc > p").text().trim()
val trailer = document.select("a.ytp-impression-link").attr("href")
val trailer = document.selectFirst("iframe#traileryt")?.attr("data-src")
val episodes = if (document.select("button.buttfilter").isNotEmpty()) {
val id = document.select("input[name=series_id]").attr("value")
@ -238,7 +236,7 @@ class NontonAnimeIDProvider : MainAPI() {
}
sources.apmap {
loadExtractor(it, data, callback)
loadExtractor(it, "$mainUrl/", callback)
}
return true

View File

@ -1,17 +1,18 @@
package com.lagradost.cloudstream3.animeproviders
import android.util.Log
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.loadExtractor
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
import com.lagradost.cloudstream3.utils.*
import org.jsoup.Jsoup
import org.jsoup.nodes.Element
import java.util.ArrayList
class OploverzProvider : MainAPI() {
override var mainUrl = "https://oploverz.asia"
override var mainUrl = "https://65.108.132.145"
override var name = "Oploverz"
override val hasQuickSearch = false
override val hasMainPage = true
override var lang = "id"
override val hasDownloadSupport = true
@ -47,7 +48,7 @@ class OploverzProvider : MainAPI() {
document.select(".bixbox.bbnofrm").forEach { block ->
val header = block.selectFirst("h3")!!.text().trim()
val animes = block.select("article[itemscope=itemscope]").mapNotNull {
val animes = block.select("article[itemscope=itemscope]").map {
it.toSearchResult()
}
if (animes.isNotEmpty()) homePageList.add(HomePageList(header, animes))
@ -86,7 +87,7 @@ class OploverzProvider : MainAPI() {
}
private fun Element.toSearchResult(): SearchResponse {
private fun Element.toSearchResult(): AnimeSearchResponse {
val href = getProperAnimeLink(this.selectFirst("a.tip")!!.attr("href"))
val title = this.selectFirst("h2[itemprop=headline]")!!.text().trim()
val posterUrl = fixUrl(this.selectFirst("img")!!.attr("src"))
@ -97,7 +98,7 @@ class OploverzProvider : MainAPI() {
return newAnimeSearchResponse(title, href, type) {
this.posterUrl = posterUrl
addDubStatus(dubExist = false, subExist = true, subEpisodes = epNum)
addSub(epNum)
}
}
@ -133,15 +134,15 @@ class OploverzProvider : MainAPI() {
.text().trim().replace("Status: ", "")
)
val typeCheck =
when {
document.select(".info-content > .spe > span:nth-child(5), .info-content > .spe > span")
.text().trim().contains("TV") -> "TV"
document.select(".info-content > .spe > span:nth-child(5), .info-content > .spe > span")
.text().trim().contains("TV") -> "Movie"
when (document.select(".info-content > .spe > span:nth-child(5), .info-content > .spe > span")
.text().trim()) {
"TV" -> "TV"
"Movie" -> "Movie"
else -> "OVA"
}
val type = getType(typeCheck)
val description = document.select(".entry-content > p").text().trim()
val trailer = document.selectFirst("a.trailerbutton")?.attr("href")
val episodes = document.select(".eplister > ul > li").map {
val name = it.select(".epl-title").text().trim()
@ -171,6 +172,7 @@ class OploverzProvider : MainAPI() {
plot = description
this.tags = tags
this.recommendations = recommendations
addTrailer(trailer)
}
}

View File

@ -0,0 +1,199 @@
package com.lagradost.cloudstream3.animeproviders
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.loadExtractor
import org.jsoup.Jsoup
import org.jsoup.nodes.Element
import java.util.ArrayList
class OtakudesuProvider : MainAPI() {
override var mainUrl = "https://otakudesu.watch"
override var name = "Otakudesu"
override val hasMainPage = true
override var lang = "id"
override val hasDownloadSupport = true
override val supportedTypes = setOf(
TvType.Anime,
TvType.AnimeMovie,
TvType.OVA
)
companion object {
fun getType(t: String): TvType {
return if (t.contains("OVA") || t.contains("Special")) TvType.OVA
else if (t.contains("Movie")) TvType.AnimeMovie
else TvType.Anime
}
fun getStatus(t: String): ShowStatus {
return when (t) {
"Completed" -> ShowStatus.Completed
"Ongoing" -> ShowStatus.Ongoing
else -> ShowStatus.Completed
}
}
}
override suspend fun getMainPage(): HomePageResponse {
val document = app.get(mainUrl).document
val homePageList = ArrayList<HomePageList>()
document.select("div.rseries").forEach { block ->
val header = block.selectFirst("div.rvad > h1")!!.text().trim()
val items = block.select("div.venz > ul > li").map {
it.toSearchResult()
}
if (items.isNotEmpty()) homePageList.add(HomePageList(header, items))
}
return HomePageResponse(homePageList)
}
private fun Element.toSearchResult(): AnimeSearchResponse {
val title = this.selectFirst("h2.jdlflm")!!.text().trim()
val href = this.selectFirst("a")!!.attr("href")
val posterUrl = this.select("div.thumbz > img").attr("src").toString()
val epNum = this.selectFirst("div.epz")?.ownText()?.replace(Regex("[^0-9]"), "")?.trim()
?.toIntOrNull()
return newAnimeSearchResponse(title, href, TvType.Anime) {
this.posterUrl = posterUrl
addSub(epNum)
}
}
override suspend fun search(query: String): List<SearchResponse> {
val link = "$mainUrl/?s=$query&post_type=anime"
val document = app.get(link).document
return document.select("ul.chivsrc > li").map {
val title = it.selectFirst("h2 > a")!!.ownText().trim()
val href = it.selectFirst("h2 > a")!!.attr("href")
val posterUrl = it.selectFirst("img")!!.attr("src").toString()
newAnimeSearchResponse(title, href, TvType.Anime) {
this.posterUrl = posterUrl
}
}
}
override suspend fun load(url: String): LoadResponse {
val document = app.get(url).document
val title = document.selectFirst("div.infozingle > p:nth-child(1) > span")?.ownText()
?.replace(":", "")?.trim().toString()
val poster = document.selectFirst("div.fotoanime > img")?.attr("src")
val tags = document.select("div.infozingle > p:nth-child(11) > span > a").map { it.text() }
val type = getType(
document.selectFirst("div.infozingle > p:nth-child(5) > span")?.ownText()
?.replace(":", "")?.trim().toString()
)
val year = Regex("\\d, ([0-9]*)").find(
document.select("div.infozingle > p:nth-child(9) > span").text()
)?.groupValues?.get(1)?.toIntOrNull()
val status = getStatus(
document.selectFirst("div.infozingle > p:nth-child(6) > span")!!.ownText()
.replace(":", "")
.trim()
)
val description = document.select("div.sinopc > p").text()
val episodes = document.select("div.episodelist")[1].select("ul > li").mapNotNull {
val name = it.selectFirst("a")!!.text().trim()
val link = fixUrl(it.selectFirst("a")!!.attr("href"))
Episode(link, name)
}.reversed()
val recommendations =
document.select("div.isi-recommend-anime-series > div.isi-konten").map {
val recName = it.selectFirst("span.judul-anime > a")!!.text()
val recHref = it.selectFirst("a")!!.attr("href")
val recPosterUrl = it.selectFirst("a > img")?.attr("src").toString()
newAnimeSearchResponse(recName, recHref, TvType.Anime) {
this.posterUrl = recPosterUrl
}
}
return newAnimeLoadResponse(title, url, type) {
engName = title
posterUrl = poster
this.year = year
addEpisodes(DubStatus.Subbed, episodes)
showStatus = status
plot = description
this.tags = tags
this.recommendations = recommendations
}
}
data class ResponseSources(
@JsonProperty("id") val id: String,
@JsonProperty("i") val i: String,
@JsonProperty("q") val q: String,
)
data class ResponseData(
@JsonProperty("data") val data: String
)
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
val document = app.get(data).document
val scriptData = document.select("script").last()?.data()
val token = scriptData?.substringAfter("{action:\"")?.substringBefore("\"}").toString()
val nonce = app.post("$mainUrl/wp-admin/admin-ajax.php", data = mapOf("action" to token))
.parsed<ResponseData>().data
val action = scriptData?.substringAfter(",action:\"")?.substringBefore("\"}").toString()
val mirrorData = document.select("div.mirrorstream > ul > li").mapNotNull {
base64Decode(it.select("a").attr("data-content"))
}.toString()
tryParseJson<List<ResponseSources>>(mirrorData)?.apmap { res ->
val id = res.id
val i = res.i
val q = res.q
var sources = Jsoup.parse(
base64Decode(
app.post(
"${mainUrl}/wp-admin/admin-ajax.php", data = mapOf(
"id" to id,
"i" to i,
"q" to q,
"nonce" to nonce,
"action" to action
)
).parsed<ResponseData>().data
)
).select("iframe").attr("src")
if (sources.startsWith("https://desustream.me")) {
if (!sources.contains("/arcg/") && !sources.contains("/odchan/") && !sources.contains("/desudrive/")) {
sources = app.get(sources).document.select("iframe").attr("src")
}
if (sources.startsWith("https://yourupload.com")) {
sources = sources.replace("//", "//www.")
}
}
loadExtractor(sources, data, callback)
}
return true
}
}

View File

@ -0,0 +1,38 @@
package com.lagradost.cloudstream3.extractors
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.*
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
class Filesim : ExtractorApi() {
override val name = "Filesim"
override val mainUrl = "https://files.im"
override val requiresReferer = false
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink> {
val sources = mutableListOf<ExtractorLink>()
with(app.get(url).document) {
this.select("script").map { script ->
if (script.data().contains("eval(function(p,a,c,k,e,d)")) {
val data = getAndUnpack(script.data()).substringAfter("sources:[").substringBefore("]")
tryParseJson<List<ResponseSource>>("[$data]")?.map {
M3u8Helper.generateM3u8(
name,
it.file,
"$mainUrl/",
).forEach { m3uData -> sources.add(m3uData) }
}
}
}
}
return sources
}
private data class ResponseSource(
@JsonProperty("file") val file: String,
@JsonProperty("type") val type: String?,
@JsonProperty("label") val label: String?
)
}

View File

@ -20,13 +20,18 @@ class Neonime8n : Hxfile() {
class KotakAnimeid : Hxfile() {
override val name = "KotakAnimeid"
override val mainUrl = "https://kotakanimeid.com"
override val requiresReferer = true
}
private data class ResponseSource(
@JsonProperty("file") val file: String,
@JsonProperty("type") val type: String?,
@JsonProperty("label") val label: String?
)
class Yufiles : Hxfile() {
override val name = "Yufiles"
override val mainUrl = "https://yufiles.com"
}
class Aico : Hxfile() {
override val name = "Aico"
override val mainUrl = "https://aico.pw"
}
open class Hxfile : ExtractorApi() {
override val name = "Hxfile"
@ -36,7 +41,7 @@ open class Hxfile : ExtractorApi() {
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink>? {
val sources = mutableListOf<ExtractorLink>()
val document = app.get(url, allowRedirects = redirect).document
val document = app.get(url, allowRedirects = redirect, referer = referer).document
with(document) {
this.select("script").map { script ->
if (script.data().contains("eval(function(p,a,c,k,e,d)")) {
@ -86,4 +91,10 @@ open class Hxfile : ExtractorApi() {
return sources
}
private data class ResponseSource(
@JsonProperty("file") val file: String,
@JsonProperty("type") val type: String?,
@JsonProperty("label") val label: String?
)
}

View File

@ -0,0 +1,81 @@
package com.lagradost.cloudstream3.extractors
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.Qualities
import com.lagradost.cloudstream3.utils.getQualityFromName
class Meownime : JWPlayer() {
override val name = "Meownime"
override val mainUrl = "https://meownime.ltd"
}
class DesuOdchan : JWPlayer() {
override val name = "DesuOdchan"
override val mainUrl = "https://desustream.me/odchan/"
}
class DesuArcg : JWPlayer() {
override val name = "DesuArcg"
override val mainUrl = "https://desustream.me/arcg/"
}
class DesuDrive : JWPlayer() {
override val name = "DesuDrive"
override val mainUrl = "https://desustream.me/desudrive/"
}
class DesuOdvip : JWPlayer() {
override val name = "DesuOdvip"
override val mainUrl = "https://desustream.me/odvip/"
}
open class JWPlayer : ExtractorApi() {
override val name = "JWPlayer"
override val mainUrl = "https://www.jwplayer.com"
override val requiresReferer = false
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink>? {
val sources = mutableListOf<ExtractorLink>()
with(app.get(url).document) {
val data = this.select("script").mapNotNull { script ->
if (script.data().contains("sources: [")) {
script.data().substringAfter("sources: [")
.substringBefore("],").replace("'", "\"")
} else if (script.data().contains("otakudesu('")) {
script.data().substringAfter("otakudesu('")
.substringBefore("');")
} else {
null
}
}
tryParseJson<List<ResponseSource>>("$data")?.map {
sources.add(
ExtractorLink(
name,
name,
it.file,
referer = url,
quality = getQualityFromName(
Regex("(\\d{3,4}p)").find(it.file)?.groupValues?.get(
1
)
)
)
)
}
}
return sources
}
private data class ResponseSource(
@JsonProperty("file") val file: String,
@JsonProperty("type") val type: String?,
@JsonProperty("label") val label: String?
)
}

View File

@ -0,0 +1,47 @@
package com.lagradost.cloudstream3.extractors
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.getQualityFromName
class YourUpload: ExtractorApi() {
override val name = "Yourupload"
override val mainUrl = "https://www.yourupload.com"
override val requiresReferer = false
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink> {
val sources = mutableListOf<ExtractorLink>()
with(app.get(url).document) {
val quality = Regex("\\d{3,4}p").find(this.select("title").text())?.groupValues?.get(0)
this.select("script").map { script ->
if (script.data().contains("var jwplayerOptions = {")) {
val data =
script.data().substringAfter("var jwplayerOptions = {").substringBefore(",\n")
val link = tryParseJson<ResponseSource>(
"{${
data.replace("file", "\"file\"").replace("'", "\"")
}}"
)
sources.add(
ExtractorLink(
source = name,
name = name,
url = link!!.file,
referer = url,
quality = getQualityFromName(quality)
)
)
}
}
}
return sources
}
private data class ResponseSource(
@JsonProperty("file") val file: String,
)
}

View File

@ -180,7 +180,7 @@ class DramaidProvider : MainAPI() {
}
tryParseJson<Tracks>(trackers)?.let {
subCallback(
subCallback.invoke(
SubtitleFile(
if (it.label.contains("Indonesia")) "${it.label}n" else it.label,
it.file

View File

@ -0,0 +1,378 @@
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 com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
import org.jsoup.nodes.Element
import java.net.URI
import java.util.ArrayList
class IdlixProvider : MainAPI() {
override var mainUrl = "https://idlix.xn--6frz82g"
override var name = "Idlix"
override val hasMainPage = true
override var lang = "id"
override val hasDownloadSupport = true
override val supportedTypes = setOf(
TvType.Movie,
TvType.TvSeries,
)
override suspend fun getMainPage(): HomePageResponse {
val document = app.get(mainUrl).document
val homePageList = ArrayList<HomePageList>()
document.select("div.items").forEach { block ->
val header =
fixTitle(block.previousElementSibling()?.previousElementSibling()?.select("header > h2")
?.text()!!.trim())
val items = block.select("article.item").mapNotNull {
it.toSearchResult()
}
if (items.isNotEmpty()) homePageList.add(HomePageList(header, items))
}
return HomePageResponse(homePageList)
}
private fun getProperLink(uri: String): String {
return when {
uri.contains("/episode/") -> {
var title = uri.substringAfter("$mainUrl/episode/")
title = Regex("(.+?)-season").find(title)?.groupValues?.get(1).toString()
"$mainUrl/tvseries/$title"
}
uri.contains("/season/") -> {
var title = uri.substringAfter("$mainUrl/season/")
title = Regex("(.+?)-season").find(title)?.groupValues?.get(1).toString()
"$mainUrl/tvseries/$title"
}
else -> {
uri
}
}
}
private fun Element.toSearchResult(): SearchResponse {
val title = this.selectFirst("h3 > a")!!.text().replace(Regex("\\(\\d{4}\\)"), "").trim()
val href = getProperLink(this.selectFirst("h3 > a")!!.attr("href"))
val posterUrl = this.select("div.poster > img").attr("src").toString()
val quality = getQualityFromString(this.select("span.quality").text())
return newMovieSearchResponse(title, href, TvType.Movie) {
this.posterUrl = posterUrl
this.quality = quality
}
}
override suspend fun search(query: String): List<SearchResponse> {
val link = "$mainUrl/search/$query"
val document = app.get(link).document
return document.select("div.result-item").map {
val title = it.selectFirst("div.title > a")!!.text().replace(Regex("\\(\\d{4}\\)"), "").trim()
val href = getProperLink(it.selectFirst("div.title > a")!!.attr("href"))
val posterUrl = it.selectFirst("img")!!.attr("src").toString()
newMovieSearchResponse(title, href, TvType.TvSeries) {
this.posterUrl = posterUrl
}
}
}
override suspend fun load(url: String): LoadResponse {
val document = app.get(url).document
val title = document.selectFirst("div.data > h1")?.text()?.replace(Regex("\\(\\d{4}\\)"), "")?.trim().toString()
val poster = document.select("div.poster > img").attr("src").toString()
val tags = document.select("div.sgeneros > a").map { it.text() }
val year = Regex(",\\s?(\\d+)").find(
document.select("span.date").text().trim()
)?.groupValues?.get(1).toString().toIntOrNull()
val tvType = if (document.select("ul#section > li:nth-child(1)").text().contains("Episodes")
) TvType.TvSeries else TvType.Movie
val description = document.select("div.wp-content > p").text().trim()
val trailer = document.selectFirst("div.embed iframe")?.attr("src")
val rating =
document.selectFirst("span.dt_rating_vgs")?.text()?.toRatingInt()
val actors = document.select("div.persons > div[itemprop=actor]").map {
Actor(it.select("meta[itemprop=name]").attr("content"), it.select("img").attr("src"))
}
val recommendations = document.select("div.owl-item").map {
val recName =
it.selectFirst("a")!!.attr("href").toString().removeSuffix("/").split("/").last()
val recHref = it.selectFirst("a")!!.attr("href")
val recPosterUrl = it.selectFirst("img")?.attr("src").toString()
newTvSeriesSearchResponse(recName, recHref, TvType.TvSeries) {
this.posterUrl = recPosterUrl
}
}
return if (tvType == TvType.TvSeries) {
val episodes = document.select("ul.episodios > li").map {
val href = it.select("a").attr("href")
val name = fixTitle(it.select("div.episodiotitle > a").text().trim())
val image = it.select("div.imagen > img").attr("src")
val episode = it.select("div.numerando").text().replace(" ", "").split("-").last()
.toIntOrNull()
val season = it.select("div.numerando").text().replace(" ", "").split("-").first()
.toIntOrNull()
Episode(
href,
name,
season,
episode,
image
)
}
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)
}
}
}
private fun getLanguage(str: String): String {
return when {
str.lowercase().contains("indonesia") || str.lowercase().contains("bahasa") -> "Indonesian"
else -> str
}
}
data class ResponseHash(
@JsonProperty("embed_url") val embed_url: String,
@JsonProperty("type") val type: String?,
)
data class ResponseSource(
@JsonProperty("hls") val hls: Boolean,
@JsonProperty("videoSource") val videoSource: String,
@JsonProperty("securedLink") val securedLink: String?,
)
data class Tracks(
@JsonProperty("kind") val kind: String?,
@JsonProperty("file") val file: String,
@JsonProperty("label") val label: String?,
)
private suspend fun invokeLokalSource(
url: String,
subCallback: (SubtitleFile) -> Unit,
sourceCallback: (ExtractorLink) -> Unit
) {
val document = app.get(url, referer = "$mainUrl/").document
val hash = url.split("/").last().substringAfter("data=")
val m3uLink = app.post(
url = "https://jeniusplay.com/player/index.php?data=$hash&do=getVideo",
data = mapOf("hash" to hash, "r" to "$mainUrl/"),
referer = url,
headers = mapOf("X-Requested-With" to "XMLHttpRequest")
).parsed<ResponseSource>().videoSource
M3u8Helper.generateM3u8(
this.name,
m3uLink,
url,
).forEach(sourceCallback)
document.select("script").map { script ->
if (script.data().contains("eval(function(p,a,c,k,e,d)")) {
val subData = getAndUnpack(script.data()).substringAfter("\"tracks\":[").substringBefore("],")
tryParseJson<List<Tracks>>("[$subData]")?.map { subtitle ->
subCallback.invoke(
SubtitleFile(
getLanguage(subtitle.label!!),
subtitle.file
)
)
}
}
}
}
data class ResponseLaviolaSource(
@JsonProperty("file") val file: String,
@JsonProperty("label") val label: String?,
)
private suspend fun invokeLaviolaSource(
url: String,
subCallback: (SubtitleFile) -> Unit,
sourceCallback: (ExtractorLink) -> Unit
) {
val document = app.get(url, referer = "$mainUrl/").document
val baseName = "Laviola"
val baseUrl = "https://laviola.live/"
document.select("script").map { script ->
if (script.data().contains("var config = {")) {
val data = script.data().substringAfter("sources: [").substringBefore("],")
tryParseJson<List<ResponseLaviolaSource>>("[$data]")?.map { m3u ->
val m3uData = app.get(m3u.file, referer = baseUrl).text
val quality =
Regex("\\d{3,4}\\.m3u8").findAll(m3uData).map { it.value }.toList()
quality.forEach {
sourceCallback.invoke(
ExtractorLink(
source = baseName,
name = baseName,
url = m3u.file.replace("video.m3u8", it),
referer = baseUrl,
quality = getQualityFromName("${it.replace(".m3u8", "")}p"),
isM3u8 = true
)
)
}
}
val subData = script.data().substringAfter("tracks: [").substringBefore("],")
tryParseJson<List<Tracks>>("[$subData]")?.map { subtitle ->
subCallback.invoke(
SubtitleFile(
getLanguage(subtitle.label!!),
(if (subtitle.kind!!.contains("captions")) subtitle.file else null)!!
)
)
}
}
}
}
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 ResponseCdn(
@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 invokeCdnSource(
url: String,
subCallback: (SubtitleFile) -> Unit,
sourceCallback: (ExtractorLink) -> Unit
) {
val domainUrl = "https://cdnplayer.online"
val id = url.trimEnd('/').split("/").last()
val sources = app.post(
url = "$domainUrl/api/source/$id",
data = mapOf("r" to mainUrl, "d" to URI(url).host)
).parsed<ResponseCdn>()
sources.data?.map {
sourceCallback.invoke(
ExtractorLink(
name,
"Cdnplayer",
fixUrl(it.file),
referer = url,
quality = getQualityFromName(it.label)
)
)
}
val userData = sources.player.poster_file.split("/")[2]
sources.captions?.map { subtitle ->
subCallback.invoke(
SubtitleFile(
getLanguage(subtitle.language),
"$domainUrl/asset/userdata/$userData/caption/${subtitle.hash}/${subtitle.id}.srt"
)
)
}
}
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
val document = app.get(data).document
val id = document.select("meta#dooplay-ajax-counter").attr("data-postid")
val type = if (data.contains("/movie/")) "movie" else "tv"
document.select("ul#playeroptionsul > li").map {
it.attr("data-nume")
}.apmap { nume ->
safeApiCall {
var source = app.post(
url = "$mainUrl/wp-admin/admin-ajax.php",
data = mapOf(
"action" to "doo_player_ajax",
"post" to id,
"nume" to nume,
"type" to type
)
).parsed<ResponseHash>().embed_url
when {
source.startsWith("https://jeniusplay.com") -> invokeLokalSource(
source,
subtitleCallback,
callback
)
source.startsWith("https://laviola.live") -> invokeLaviolaSource(
source,
subtitleCallback,
callback
)
source.startsWith("https://cdnplayer.online") -> invokeCdnSource(
source,
subtitleCallback,
callback
)
else -> {
if (source.startsWith("https://uservideo.xyz")) {
source = app.get(source).document.select("iframe").attr("src")
}
loadExtractor(source, data, callback)
}
}
}
}
return true
}
}

View File

@ -1,6 +1,8 @@
package com.lagradost.cloudstream3.movieproviders
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.LoadResponse.Companion.addActors
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.loadExtractor
import org.jsoup.Jsoup
@ -25,7 +27,7 @@ class LayarKacaProvider : MainAPI() {
val homePageList = ArrayList<HomePageList>()
document.select("section.hot-block,section#newseries").forEach { block ->
val header = block.select("footer.load-more > a").text().trim()
val header = fixTitle(block.select("footer.load-more > a").text().trim())
val items = block.select("div.slider-item").mapNotNull {
it.toTopSearchResult()
}
@ -33,7 +35,7 @@ class LayarKacaProvider : MainAPI() {
}
document.select("div#newest").forEach { block ->
val header = block.select(".header > h2 > a").text()
val header = fixTitle(block.select(".header > h2 > a").text())
val items = block.select("div.item").mapNotNull {
it.toMainSearchResult()
}
@ -42,7 +44,7 @@ class LayarKacaProvider : MainAPI() {
document.select("section#recomendation,section#populer,section#seriespopuler")
.forEach { block ->
val header = block.select(".header > h2 > a").text()
val header = fixTitle(block.select(".header > h2 > a").text())
val items = block.select("div.item").mapNotNull {
it.toBottomSearchResult()
}
@ -60,7 +62,7 @@ class LayarKacaProvider : MainAPI() {
if (this.select("div.quality-top").isNotEmpty()) TvType.Movie else TvType.TvSeries
return if (type == TvType.Movie) {
val quality = getQualityFromString(this.select("div.quality-top").text().trim())
return newMovieSearchResponse(title, href, TvType.Movie) {
newMovieSearchResponse(title, href, TvType.Movie) {
this.posterUrl = posterUrl
this.quality = quality
}
@ -68,7 +70,7 @@ class LayarKacaProvider : MainAPI() {
val episode = this.select("div.last-episode > span").text().toIntOrNull()
newAnimeSearchResponse(title, href, TvType.TvSeries) {
this.posterUrl = posterUrl
addDubStatus(dubExist = false, subExist = true, subEpisodes = episode)
addSub(episode)
}
}
@ -133,15 +135,10 @@ class LayarKacaProvider : MainAPI() {
.isNotEmpty()
) TvType.TvSeries else TvType.Movie
val description = document.select("div.content > blockquote").text().trim()
val trailer = document.selectFirst("div.action-player li > a.fancybox")?.attr("href")
val rating =
document.selectFirst("div.content > div:nth-child(6) > h3")?.text()?.toRatingInt()
val actors = document.select("div.col-xs-9.content > div:nth-child(3) > h3 > a").map {
ActorData(
Actor(
it.text()
)
)
}
val actors = document.select("div.col-xs-9.content > div:nth-child(3) > h3 > a").map { it.text() }
val recommendations = document.select("div.row.item-media").map {
val recName = it.selectFirst("h3")?.text()?.trim().toString()
@ -171,8 +168,9 @@ class LayarKacaProvider : MainAPI() {
this.plot = description
this.tags = tags
this.rating = rating
this.actors = actors
addActors(actors)
this.recommendations = recommendations
addTrailer(trailer)
}
}
else {
@ -182,8 +180,9 @@ class LayarKacaProvider : MainAPI() {
this.plot = description
this.tags = tags
this.rating = rating
this.actors = actors
addActors(actors)
this.recommendations = recommendations
addTrailer(trailer)
}
}
}

View File

@ -0,0 +1,206 @@
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.utils.AppUtils.tryParseJson
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.getQualityFromName
import org.jsoup.nodes.Element
import java.util.ArrayList
class MultiplexProvider : MainAPI() {
override var mainUrl = "https://146.19.24.137"
override var name = "Multiplex"
override val hasMainPage = true
override var lang = "id"
override val hasDownloadSupport = true
override val supportedTypes = setOf(
TvType.Movie,
TvType.TvSeries,
TvType.AsianDrama
)
override suspend fun getMainPage(): HomePageResponse {
val document = app.get(mainUrl).document
val homePageList = ArrayList<HomePageList>()
document.select("div.col-md-12 > div.home-widget").forEach { block ->
val header = fixTitle(block.select("h3.homemodule-title").text())
val items = block.select("div.col-md-125").mapNotNull {
it.toSearchResult()
}
if (items.isNotEmpty()) homePageList.add(HomePageList(header, items))
}
document.select("div.container.gmr-maincontent")
.forEach { block ->
val header = fixTitle(block.select("h3.homemodule-title").text())
val items = block.select("article.item").mapNotNull {
it.toSearchResult()
}
if (items.isNotEmpty()) homePageList.add(HomePageList(header, items))
}
document.select("div#idmuvi-rp-2").forEach { block ->
val header = fixTitle(block.selectFirst("h3.widget-title")?.ownText()!!.trim())
val items = block.select("div.idmuvi-rp ul li").mapNotNull {
it.toBottomSearchResult()
}
if (items.isNotEmpty()) homePageList.add(HomePageList(header, items))
}
return HomePageResponse(homePageList)
}
private fun Element.toSearchResult(): SearchResponse {
val title = this.selectFirst("h2.entry-title > a")!!.text().trim()
val href = this.selectFirst("a")!!.attr("href")
val posterUrl = fixUrl(this.selectFirst("a > img")?.attr("data-src").toString())
val quality = getQualityFromString(this.select("div.gmr-quality-item > a").text().trim())
return if (quality == null) {
val episode = this.select("div.gmr-numbeps > span").text().toIntOrNull()
newAnimeSearchResponse(title, href, TvType.TvSeries) {
this.posterUrl = posterUrl
addSub(episode)
}
} else {
newMovieSearchResponse(title, href, TvType.Movie) {
this.posterUrl = posterUrl
this.quality = quality
}
}
}
private fun Element.toBottomSearchResult(): SearchResponse {
val title = this.selectFirst("a > span.idmuvi-rp-title")!!.text().trim()
val href = this.selectFirst("a")!!.attr("href")
val posterUrl = fixUrl(this.selectFirst("a > img")?.attr("data-src").toString())
return newMovieSearchResponse(title, href, TvType.Movie) {
this.posterUrl = posterUrl
}
}
override suspend fun search(query: String): List<SearchResponse> {
val link = "$mainUrl/?s=$query&post_type[]=post&post_type[]=tv"
val document = app.get(link).document
return document.select("div#gmr-main-load > article.item").map {
it.toSearchResult()
}
}
override suspend fun load(url: String): LoadResponse {
val document = app.get(url).document
val title =
document.selectFirst("h1.entry-title")?.text()?.substringBefore("Season")?.trim()
.toString()
val poster =
fixUrl(document.selectFirst("figure.pull-left > img")?.attr("data-src").toString())
val tags = document.select("span.gmr-movie-genre:contains(Genre:) > a").map { it.text() }
val year =
document.select("span.gmr-movie-genre:contains(Year:) > a").text().trim().toIntOrNull()
val tvType = if (url.contains("/tv/")) TvType.TvSeries else TvType.Movie
val description = document.selectFirst("div[itemprop=description] > p")?.text()?.trim()
val trailer = document.selectFirst("ul.gmr-player-nav li a.gmr-trailer-popup")?.attr("href")
val rating =
document.selectFirst("div.gmr-meta-rating > span[itemprop=ratingValue]")?.text()
?.toRatingInt()
val actors = document.select("div.gmr-moviedata").last()?.select("span[itemprop=actors]")?.map { it.select("a").text() }
val recommendations = document.select("div.idmuvi-rp ul li").map {
it.toBottomSearchResult()
}
return if (tvType == TvType.TvSeries) {
val episodes = document.select("div.gmr-listseries > a").map {
val href = fixUrl(it.attr("href"))
val episode = it.text().split(" ").last().toIntOrNull()
val season = it.text().split(" ").first().substringAfter("S").toIntOrNull()
Episode(
href,
"Episode $episode",
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 {
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)
}
}
}
private data class ResponseSource(
@JsonProperty("file") val file: String,
@JsonProperty("type") val type: String?,
@JsonProperty("label") val label: String?
)
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
val document = app.get(data).document
val id = document.selectFirst("div#muvipro_player_content_id")!!.attr("data-id")
val server = app.post(
"$mainUrl/wp-admin/admin-ajax.php",
data = mapOf("action" to "muvipro_player_content", "tab" to "player1", "post_id" to id)
).document.select("iframe").attr("src")
app.get(server, referer = "$mainUrl/").document.select("script").map { script ->
if (script.data().contains("var config = {")) {
val source = script.data().substringAfter("sources: [").substringBefore("],")
tryParseJson<List<ResponseSource>>("[$source]")?.map { m3u ->
val m3uData = app.get(m3u.file, referer = "https://gdriveplayer.link/").text
val quality =
Regex("\\d{3,4}\\.m3u8").findAll(m3uData).map { it.value }.toList()
quality.forEach {
callback.invoke(
ExtractorLink(
source = name,
name = name,
url = m3u.file.replace("video.m3u8", it),
referer = "https://gdriveplayer.link/",
quality = getQualityFromName("${it.replace(".m3u8", "")}p"),
isM3u8 = true
)
)
}
}
}
}
return true
}
}

View File

@ -2,6 +2,8 @@ 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.logError
import com.lagradost.cloudstream3.mvvm.safeApiCall
import com.lagradost.cloudstream3.network.WebViewResolver
@ -106,16 +108,11 @@ class RebahinProvider : MainAPI() {
)?.groupValues?.get(1).toString().toIntOrNull()
val tvType = if (url.contains("/series/")) TvType.TvSeries else TvType.Movie
val description = document.select("span[itemprop=reviewBody] > p").text().trim()
val trailer = fixUrlNull(document.selectFirst("div.modal-body-trailer iframe")?.attr("src"))
val rating = document.selectFirst("span[itemprop=ratingValue]")?.text()?.toRatingInt()
val duration = document.selectFirst(".mvici-right > p:nth-child(1)")!!
.ownText().replace(Regex("[^0-9]"), "").toIntOrNull()
val actors = document.select("span[itemprop=actor] > a").map {
ActorData(
Actor(
it.select("span").text()
)
)
}
val actors = document.select("span[itemprop=actor] > a").map { it.select("span").text() }
return if (tvType == TvType.TvSeries) {
val baseLink = document.select("div#mv-info > a").attr("href")
@ -124,7 +121,6 @@ class RebahinProvider : MainAPI() {
name
}.distinct().map {
val name = it
// val epNum = Regex("[^r|R]\\s(\\d+)").find(it)?.groupValues?.get(1)?.toIntOrNull()
val epNum = it.replace(Regex("[^0-9]"), "").toIntOrNull()
val link = "$baseLink?ep=$epNum"
newEpisode(link) {
@ -139,7 +135,8 @@ class RebahinProvider : MainAPI() {
this.tags = tags
this.rating = rating
this.duration = duration
this.actors = actors
addActors(actors)
addTrailer(trailer)
}
} else {
val episodes = document.select("div#mv-info > a").attr("href")
@ -150,7 +147,8 @@ class RebahinProvider : MainAPI() {
this.tags = tags
this.rating = rating
this.duration = duration
this.actors = actors
addActors(actors)
addTrailer(trailer)
}
}
}
@ -204,7 +202,7 @@ class RebahinProvider : MainAPI() {
val trackJson = script.data().substringAfter("tracks: [").substringBefore("],")
val track = tryParseJson<List<Tracks>>("[$trackJson]")
track?.map {
subCallback(
subCallback.invoke(
SubtitleFile(
"Indonesian",
(if (it.file.contains(".srt")) it.file else null)!!
@ -262,7 +260,7 @@ class RebahinProvider : MainAPI() {
}
val userData = sources.player.poster_file.split("/")[2]
sources.captions?.map {
subCallback(
subCallback.invoke(
SubtitleFile(
if (it.language.lowercase().contains("eng")) it.language else "Indonesian",
"$domainUrl/asset/userdata/$userData/caption/${it.hash}/${it.id}.srt"

View File

@ -0,0 +1,252 @@
package com.lagradost.cloudstream3.syncproviders.providers
import android.util.Log
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.subtitles.AbstractSubProvider
import com.lagradost.cloudstream3.subtitles.AbstractSubtitleEntities
import com.lagradost.cloudstream3.syncproviders.AuthAPI
import com.lagradost.cloudstream3.utils.SubtitleHelper
class IndexSubtitleApi : AuthAPI, AbstractSubProvider {
override val name = "IndexSubtitle"
override val idPrefix = "indexsubtitle"
override val requiresLogin = false
override val icon: Nothing? = null
override val createAccountUrl: Nothing? = null
override fun loginInfo(): Nothing? = null
override fun logOut() {}
companion object {
const val host = "https://indexsubtitle.com"
const val TAG = "INDEXSUBS"
}
private fun fixUrl(url: String): String {
if (url.startsWith("http")) {
return url
}
if (url.isEmpty()) {
return ""
}
val startsWithNoHttp = url.startsWith("//")
if (startsWithNoHttp) {
return "https:$url"
} else {
if (url.startsWith('/')) {
return host + url
}
return "$host/$url"
}
}
private fun getOrdinal(num: Int?) : String? {
return when (num) {
1 -> "First"
2 -> "Second"
3 -> "Third"
4 -> "Fourth"
5 -> "Fifth"
6 -> "Sixth"
7 -> "Seventh"
8 -> "Eighth"
9 -> "Ninth"
10 -> "Tenth"
11 -> "Eleventh"
12 -> "Twelfth"
13 -> "Thirteenth"
14 -> "Fourteenth"
15 -> "Fifteenth"
16 -> "Sixteenth"
17 -> "Seventeenth"
18 -> "Eighteenth"
19 -> "Nineteenth"
20 -> "Twentieth"
21 -> "Twenty-First"
22 -> "Twenty-Second"
23 -> "Twenty-Third"
24 -> "Twenty-Fourth"
25 -> "Twenty-Fifth"
26 -> "Twenty-Sixth"
27 -> "Twenty-Seventh"
28 -> "Twenty-Eighth"
29 -> "Twenty-Ninth"
30 -> "Thirtieth"
31 -> "Thirty-First"
32 -> "Thirty-Second"
33 -> "Thirty-Third"
34 -> "Thirty-Fourth"
35 -> "Thirty-Fifth"
else -> null
}
}
private fun isRightEps(text: String, seasonNum: Int?, epNum: Int?) : Boolean {
val FILTER_EPS_REGEX = Regex("(?i)((Chapter\\s?0?${epNum})|((Season)?\\s?0?${seasonNum}?\\s?(Episode)\\s?0?${epNum}[^0-9]))|(?i)((S?0?${seasonNum}?E0?${epNum}[^0-9])|(0?${seasonNum}[a-z]0?${epNum}[^0-9]))")
return text.contains(FILTER_EPS_REGEX)
}
private fun haveEps(text: String) : Boolean {
val HAVE_EPS_REGEX = Regex("(?i)((Chapter\\s?0?\\d)|((Season)?\\s?0?\\d?\\s?(Episode)\\s?0?\\d))|(?i)((S?0?\\d?E0?\\d)|(0?\\d[a-z]0?\\d))")
return text.contains(HAVE_EPS_REGEX)
}
override suspend fun search(query: AbstractSubtitleEntities.SubtitleSearch): List<AbstractSubtitleEntities.SubtitleEntity> {
val imdbId = query.imdb ?: 0
val lang = query.lang
val queryLang = SubtitleHelper.fromTwoLettersToLanguage(lang.toString())
val queryText = query.query
val epNum = query.epNumber ?: 0
val seasonNum = query.seasonNumber ?: 0
val yearNum = query.year ?: 0
val urlItems = ArrayList<String>()
fun cleanResources(results: MutableList<AbstractSubtitleEntities.SubtitleEntity>, name: String, link: String) {
results.add(
AbstractSubtitleEntities.SubtitleEntity(
idPrefix = idPrefix,
name = name,
lang = queryLang.toString(),
data = link,
type = if (seasonNum > 0) TvType.TvSeries else TvType.Movie,
epNumber = epNum,
seasonNumber = seasonNum,
year = yearNum
)
)
}
val document = app.get("$host/?search=$queryText").document
document.select("div.my-3.p-3 div.media").map { block ->
if (seasonNum > 0) {
val name = block.select("strong.text-primary").text().trim()
val season = getOrdinal(seasonNum)
if ((block.selectFirst("a")?.attr("href")
?.contains(
"$season",
ignoreCase = true
)!! || name.contains(
"$season",
ignoreCase = true
)) && name.contains(queryText, ignoreCase = true)
) {
block.select("div.media").mapNotNull {
urlItems.add(
fixUrl(
it.selectFirst("a")!!.attr("href")
)
)
}
}
} else {
if (block.selectFirst("strong")!!.text().trim()
.matches(Regex("(?i)^$queryText\$"))
) {
if (block.select("span[title=Release]").isNullOrEmpty()) {
block.select("div.media").mapNotNull {
val urlItem = fixUrl(
it.selectFirst("a")!!.attr("href")
)
val itemDoc = app.get(urlItem).document
val id = imdbUrlToIdNullable(itemDoc.selectFirst("div.d-flex span.badge.badge-primary")?.parent()
?.attr("href"))?.toLongOrNull()
val year = itemDoc.selectFirst("div.d-flex span.badge.badge-success")
?.ownText()
?.trim().toString()
Log.i(TAG, "id => $id \nyear => $year||$yearNum")
if (imdbId > 0) {
if (id == imdbId) {
urlItems.add(urlItem)
}
} else {
if (year.contains("$yearNum")) {
urlItems.add(urlItem)
}
}
}
} else {
if (block.select("span[title=Release]").text().trim()
.contains("$yearNum")
) {
block.select("div.media").mapNotNull {
urlItems.add(
fixUrl(
it.selectFirst("a")!!.attr("href")
)
)
}
}
}
}
}
}
Log.i(TAG, "urlItems => $urlItems")
val results = mutableListOf<AbstractSubtitleEntities.SubtitleEntity>()
urlItems.forEach { url ->
val request = app.get(url)
if (request.isSuccessful) {
request.document.select("div.my-3.p-3 div.media").map { block ->
if (block.select("span.d-block span[data-original-title=Language]").text().trim()
.contains("$queryLang")
) {
var name = block.select("strong.text-primary").text().trim()
val link = fixUrl(block.selectFirst("a")!!.attr("href"))
if(seasonNum > 0) {
when {
isRightEps(name, seasonNum, epNum) -> {
cleanResources(results, name, link)
}
!(haveEps(name)) -> {
name = "$name (S${seasonNum}:E${epNum})"
cleanResources(results, name, link)
}
}
} else {
cleanResources(results, name, link)
}
}
}
}
}
return results
}
override suspend fun load(data: AbstractSubtitleEntities.SubtitleEntity): String? {
val seasonNum = data.seasonNumber
val epNum = data.epNumber
val req = app.get(data.data)
if(req.isSuccessful) {
val document = req.document
val link = if (document.select("div.my-3.p-3 div.media").size == 1) {
fixUrl(
document.selectFirst("div.my-3.p-3 div.media a")!!.attr("href")
)
} else {
document.select("div.my-3.p-3 div.media").mapNotNull { block ->
val name = block.selectFirst("strong.d-block.text-primary")?.text()?.trim().toString()
if (seasonNum!! > 0) {
if (isRightEps(name, seasonNum, epNum)) {
fixUrl(block.selectFirst("a")!!.attr("href"))
} else {
null
}
} else {
fixUrl(block.selectFirst("a")!!.attr("href"))
}
}.first()
}
return link
}
return null
}
}

View File

@ -216,11 +216,23 @@ val extractorApis: Array<ExtractorApi> = arrayOf(
Blogger(),
Solidfiles(),
YourUpload(),
Hxfile(),
KotakAnimeid(),
Neonime8n(),
Neonime7n(),
Yufiles(),
Aico(),
JWPlayer(),
Meownime(),
DesuArcg(),
DesuOdchan(),
DesuOdvip(),
DesuDrive(),
Filesim(),
YoutubeExtractor(),
YoutubeShortLinkExtractor(),