Merge branch 'master' into adityajd-patch-1

This commit is contained in:
adityajd 2023-12-14 10:01:27 +07:00 committed by GitHub
commit 81e43f707f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
40 changed files with 704 additions and 816 deletions

View File

@ -43,10 +43,6 @@ jobs:
- name: Access Secrets - name: Access Secrets
env: env:
TMDB_API: ${{ secrets.TMDB_API }} TMDB_API: ${{ secrets.TMDB_API }}
SORA_API: ${{ secrets.SORA_API }}
SORAHE: ${{ secrets.SORAHE }}
SORAXA: ${{ secrets.SORAXA }}
SORATED: ${{ secrets.SORATED }}
DUMP_API: ${{ secrets.DUMP_API }} DUMP_API: ${{ secrets.DUMP_API }}
DUMP_KEY: ${{ secrets.DUMP_KEY }} DUMP_KEY: ${{ secrets.DUMP_KEY }}
CRUNCHYROLL_BASIC_TOKEN: ${{ secrets.CRUNCHYROLL_BASIC_TOKEN }} CRUNCHYROLL_BASIC_TOKEN: ${{ secrets.CRUNCHYROLL_BASIC_TOKEN }}
@ -55,16 +51,13 @@ jobs:
ANICHI_SERVER: ${{ secrets.ANICHI_SERVER }} ANICHI_SERVER: ${{ secrets.ANICHI_SERVER }}
ANICHI_ENDPOINT: ${{ secrets.ANICHI_ENDPOINT }} ANICHI_ENDPOINT: ${{ secrets.ANICHI_ENDPOINT }}
ANICHI_APP: ${{ secrets.ANICHI_APP }} ANICHI_APP: ${{ secrets.ANICHI_APP }}
PRIMEWIRE_KEY: ${{ secrets.PRIMEWIRE_KEY }}
ZSHOW_API: ${{ secrets.ZSHOW_API }} ZSHOW_API: ${{ secrets.ZSHOW_API }}
SFMOVIES_API: ${{ secrets.SFMOVIES_API }} SFMOVIES_API: ${{ secrets.SFMOVIES_API }}
CINEMATV_API: ${{ secrets.CINEMATV_API }}
OMOVIES_API: ${{ secrets.OMOVIES_API }}
run: | run: |
cd $GITHUB_WORKSPACE/src cd $GITHUB_WORKSPACE/src
echo SORA_API=$SORA_API >> local.properties
echo TMDB_API=$TMDB_API >> local.properties echo TMDB_API=$TMDB_API >> local.properties
echo SORAHE=$SORAHE >> local.properties
echo SORAXA=$SORAXA >> local.properties
echo SORATED=$SORATED >> local.properties
echo DUMP_API=$DUMP_API >> local.properties echo DUMP_API=$DUMP_API >> local.properties
echo DUMP_KEY=$DUMP_KEY >> local.properties echo DUMP_KEY=$DUMP_KEY >> local.properties
echo CRUNCHYROLL_BASIC_TOKEN=$CRUNCHYROLL_BASIC_TOKEN >> local.properties echo CRUNCHYROLL_BASIC_TOKEN=$CRUNCHYROLL_BASIC_TOKEN >> local.properties
@ -73,9 +66,10 @@ jobs:
echo ANICHI_SERVER=$ANICHI_SERVER >> local.properties echo ANICHI_SERVER=$ANICHI_SERVER >> local.properties
echo ANICHI_ENDPOINT=$ANICHI_ENDPOINT >> local.properties echo ANICHI_ENDPOINT=$ANICHI_ENDPOINT >> local.properties
echo ANICHI_APP=$ANICHI_APP >> local.properties echo ANICHI_APP=$ANICHI_APP >> local.properties
echo PRIMEWIRE_KEY=$PRIMEWIRE_KEY >> local.properties
echo ZSHOW_API=$ZSHOW_API >> local.properties echo ZSHOW_API=$ZSHOW_API >> local.properties
echo SFMOVIES_API=$SFMOVIES_API >> local.properties echo SFMOVIES_API=$SFMOVIES_API >> local.properties
echo CINEMATV_API=$CINEMATV_API >> local.properties
echo OMOVIES_API=$OMOVIES_API >> local.properties
- name: Build Plugins - name: Build Plugins
run: | run: |
@ -85,10 +79,10 @@ jobs:
cp **/build/*.cs3 $GITHUB_WORKSPACE/builds cp **/build/*.cs3 $GITHUB_WORKSPACE/builds
cp build/plugins.json $GITHUB_WORKSPACE/builds cp build/plugins.json $GITHUB_WORKSPACE/builds
- name: Move Loklok - name: Move Kuramanime
run: | run: |
rm $GITHUB_WORKSPACE/builds/Loklok.cs3 || true rm $GITHUB_WORKSPACE/builds/KuramanimeProvider.cs3 || true
cp $GITHUB_WORKSPACE/builds/stored/Loklok.cs3 $GITHUB_WORKSPACE/builds cp $GITHUB_WORKSPACE/builds/stored/KuramanimeProvider.cs3 $GITHUB_WORKSPACE/builds
- name: Push builds - name: Push builds
run: | run: |

View File

@ -1,5 +1,5 @@
// use an integer for version numbers // use an integer for version numbers
version = 6 version = 8
cloudstream { cloudstream {

View File

@ -138,7 +138,7 @@ class Animasu : MainAPI() {
document.select(".mobius > .mirror > option").mapNotNull { document.select(".mobius > .mirror > option").mapNotNull {
fixUrl(Jsoup.parse(base64Decode(it.attr("value"))).select("iframe").attr("src")) to it.text() fixUrl(Jsoup.parse(base64Decode(it.attr("value"))).select("iframe").attr("src")) to it.text()
}.apmap { (iframe, quality) -> }.apmap { (iframe, quality) ->
loadFixedExtractor(iframe, quality, "$mainUrl/", subtitleCallback, callback) loadFixedExtractor(iframe.fixIframe(), quality, "$mainUrl/", subtitleCallback, callback)
} }
return true return true
} }
@ -157,7 +157,7 @@ class Animasu : MainAPI() {
link.name, link.name,
link.url, link.url,
link.referer, link.referer,
if(link.type != ExtractorLinkType.M3U8) getIndexQuality(quality) else link.quality, if(link.type == ExtractorLinkType.M3U8 || link.name == "Uservideo") link.quality else getIndexQuality(quality),
link.type, link.type,
link.headers, link.headers,
link.extractorData link.extractorData
@ -166,6 +166,14 @@ class Animasu : MainAPI() {
} }
} }
private fun String.fixIframe() : String {
return if(this.startsWith("https://dl.berkasdrive.com")) {
base64Decode(this.substringAfter("id="))
} else {
this
}
}
private fun getIndexQuality(str: String?): Int { private fun getIndexQuality(str: String?): Int {
return Regex("(\\d{3,4})[pP]").find(str ?: "")?.groupValues?.getOrNull(1)?.toIntOrNull() return Regex("(\\d{3,4})[pP]").find(str ?: "")?.groupValues?.getOrNull(1)?.toIntOrNull()
?: Qualities.Unknown.value ?: Qualities.Unknown.value

View File

@ -4,12 +4,9 @@ import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.LoadResponse.Companion.addAniListId import com.lagradost.cloudstream3.LoadResponse.Companion.addAniListId
import com.lagradost.cloudstream3.LoadResponse.Companion.addMalId import com.lagradost.cloudstream3.LoadResponse.Companion.addMalId
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
import com.lagradost.cloudstream3.network.CloudflareKiller
import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.httpsify import com.lagradost.cloudstream3.utils.httpsify
import com.lagradost.cloudstream3.utils.loadExtractor import com.lagradost.cloudstream3.utils.loadExtractor
import okhttp3.Interceptor
import okhttp3.Response
import org.jsoup.Jsoup import org.jsoup.Jsoup
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
@ -18,26 +15,12 @@ class AnimeIndoProvider : MainAPI() {
override var name = "AnimeIndo" override var name = "AnimeIndo"
override val hasMainPage = true override val hasMainPage = true
override var lang = "id" override var lang = "id"
private val cloudflareKiller by lazy { CloudflareKiller() }
private val interceptor by lazy { CloudflareInterceptor(cloudflareKiller) }
override val supportedTypes = setOf( override val supportedTypes = setOf(
TvType.Anime, TvType.Anime,
TvType.AnimeMovie, TvType.AnimeMovie,
TvType.OVA TvType.OVA
) )
class CloudflareInterceptor(private val cloudflareKiller: CloudflareKiller): Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
val response = chain.proceed(request)
val doc = Jsoup.parse(response.peekBody(1024 * 1024).string())
if (doc.select("title").text() == "Just a moment...") {
return cloudflareKiller.intercept(chain)
}
return response
}
}
companion object { companion object {
fun getType(t: String): TvType { fun getType(t: String): TvType {
return if (t.contains("OVA", true) || t.contains("Special")) TvType.OVA return if (t.contains("OVA", true) || t.contains("Special")) TvType.OVA
@ -66,8 +49,7 @@ class AnimeIndoProvider : MainAPI() {
page: Int, page: Int,
request: MainPageRequest request: MainPageRequest
): HomePageResponse { ): HomePageResponse {
val url = "$mainUrl/${request.data}/page/$page" val document = app.get("$mainUrl/${request.data}/page/$page").document
val document = app.get(url, interceptor = interceptor).document
val home = document.select("main#main div.animposx").mapNotNull { val home = document.select("main#main div.animposx").mapNotNull {
it.toSearchResult() it.toSearchResult()
} }
@ -101,7 +83,6 @@ class AnimeIndoProvider : MainAPI() {
return newAnimeSearchResponse(title, href, TvType.Anime) { return newAnimeSearchResponse(title, href, TvType.Anime) {
this.posterUrl = posterUrl this.posterUrl = posterUrl
addSub(epNum) addSub(epNum)
posterHeaders = cloudflareKiller.getCookieHeaders(mainUrl).toMap()
} }
} }
@ -109,8 +90,7 @@ class AnimeIndoProvider : MainAPI() {
override suspend fun search(query: String): List<SearchResponse> { override suspend fun search(query: String): List<SearchResponse> {
val anime = mutableListOf<SearchResponse>() val anime = mutableListOf<SearchResponse>()
(1..2).forEach { page -> (1..2).forEach { page ->
val link = "$mainUrl/page/$page/?s=$query" val document = app.get("$mainUrl/page/$page/?s=$query").document
val document = app.get(link, interceptor = interceptor).document
val media = document.select(".site-main.relat > article").mapNotNull { val media = document.select(".site-main.relat > article").mapNotNull {
val title = it.selectFirst("div.title > h2")!!.ownText().trim() val title = it.selectFirst("div.title > h2")!!.ownText().trim()
val href = it.selectFirst("a")!!.attr("href") val href = it.selectFirst("a")!!.attr("href")
@ -118,7 +98,6 @@ class AnimeIndoProvider : MainAPI() {
val type = getType(it.select("div.type").text().trim()) val type = getType(it.select("div.type").text().trim())
newAnimeSearchResponse(title, href, type) { newAnimeSearchResponse(title, href, type) {
this.posterUrl = posterUrl this.posterUrl = posterUrl
posterHeaders = cloudflareKiller.getCookieHeaders(mainUrl).toMap()
} }
} }
if(media.isNotEmpty()) anime.addAll(media) if(media.isNotEmpty()) anime.addAll(media)
@ -127,8 +106,7 @@ class AnimeIndoProvider : MainAPI() {
} }
override suspend fun load(url: String): LoadResponse? { override suspend fun load(url: String): LoadResponse? {
val document = app.get(url, interceptor = interceptor).document val document = app.get(url).document
val title = document.selectFirst("h1.entry-title")?.text()?.replace("Subtitle Indonesia", "") val title = document.selectFirst("h1.entry-title")?.text()?.replace("Subtitle Indonesia", "")
?.trim() ?: return null ?.trim() ?: return null
val poster = document.selectFirst("div.thumb > img[itemprop=image]")?.attr("src") val poster = document.selectFirst("div.thumb > img[itemprop=image]")?.attr("src")
@ -168,7 +146,6 @@ class AnimeIndoProvider : MainAPI() {
addTrailer(trailer) addTrailer(trailer)
addMalId(tracker?.malId) addMalId(tracker?.malId)
addAniListId(tracker?.aniId?.toIntOrNull()) addAniListId(tracker?.aniId?.toIntOrNull())
posterHeaders = cloudflareKiller.getCookieHeaders(mainUrl).toMap()
} }
} }
@ -179,12 +156,12 @@ class AnimeIndoProvider : MainAPI() {
callback: (ExtractorLink) -> Unit callback: (ExtractorLink) -> Unit
): Boolean { ): Boolean {
val document = app.get(data, interceptor = interceptor).document val document = app.get(data).document
document.select("div.itemleft > .mirror > option").mapNotNull { document.select("div.itemleft > .mirror > option").mapNotNull {
fixUrl(Jsoup.parse(base64Decode(it.attr("value"))).select("iframe").attr("src")) fixUrl(Jsoup.parse(base64Decode(it.attr("value"))).select("iframe").attr("src"))
}.apmap { }.apmap {
if (it.startsWith(mainUrl)) { if (it.startsWith(mainUrl)) {
app.get(it, referer = "$mainUrl/", interceptor = interceptor).document.select("iframe").attr("src") app.get(it, referer = "$mainUrl/").document.select("iframe").attr("src")
} else { } else {
it it
} }

View File

@ -1,7 +1,7 @@
import org.jetbrains.kotlin.konan.properties.Properties import org.jetbrains.kotlin.konan.properties.Properties
// use an integer for version numbers // use an integer for version numbers
version = 28 version = 29
android { android {
defaultConfig { defaultConfig {

View File

@ -6,8 +6,12 @@ import com.lagradost.cloudstream3.utils.httpsify
import com.lagradost.cloudstream3.utils.loadExtractor import com.lagradost.cloudstream3.utils.loadExtractor
class DutaMovie : Gomov() { class DutaMovie : Gomov() {
override var mainUrl = "https://cinema.dutamovie21.vip" override var mainUrl = "https://cinema.dutamovie21.vip"
override var name = "DutaMovie"
override var name = "DutaMovie"
override val mainPage = mainPageOf( override val mainPage = mainPageOf(
"category/box-office/page/%d/" to "Box Office", "category/box-office/page/%d/" to "Box Office",
"category/serial-tv/page/%d/" to "Serial TV", "category/serial-tv/page/%d/" to "Serial TV",

View File

@ -1,21 +1,14 @@
package com.hexated package com.hexated
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.ErrorLoadingException
import com.lagradost.cloudstream3.SubtitleFile import com.lagradost.cloudstream3.SubtitleFile
import com.lagradost.cloudstream3.apmap import com.lagradost.cloudstream3.apmap
import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.base64Decode import com.lagradost.cloudstream3.base64Decode
import com.lagradost.cloudstream3.extractors.* import com.lagradost.cloudstream3.extractors.*
import com.lagradost.cloudstream3.extractors.helper.AesHelper
import com.lagradost.cloudstream3.utils.AppUtils
import com.lagradost.cloudstream3.utils.ExtractorApi import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.M3u8Helper import com.lagradost.cloudstream3.utils.M3u8Helper
import com.lagradost.cloudstream3.utils.loadExtractor import com.lagradost.cloudstream3.utils.loadExtractor
import org.jsoup.nodes.Document
import org.mozilla.javascript.Context
import org.mozilla.javascript.Scriptable
open class Uplayer : ExtractorApi() { open class Uplayer : ExtractorApi() {
override val name = "Uplayer" override val name = "Uplayer"
@ -94,81 +87,4 @@ class Likessb : StreamSB() {
class DbGdriveplayer : Gdriveplayer() { class DbGdriveplayer : Gdriveplayer() {
override var mainUrl = "https://database.gdriveplayer.us" override var mainUrl = "https://database.gdriveplayer.us"
}
object NineTv {
suspend fun getUrl(
url: String,
referer: String?,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
) {
val mainUrl = getBaseUrl(url)
val res = app.get(url, headers = mapOf(
"Accept" to "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
"Accept-Language" to "en-US,en;q=0.5",
"Referer" to (referer ?: ""),
))
val master = Regex("\\s*=\\s*'([^']+)").find(res.text)?.groupValues?.get(1)
val key = "tSIsE8FgpRkv3QQQ"
val decrypt = AesHelper.cryptoAESHandler(master ?: return, key.toByteArray(), false)
?.replace("\\", "")
?: throw ErrorLoadingException("failed to decrypt")
val source = Regex(""""?file"?:\s*"([^"]+)""").find(decrypt)?.groupValues?.get(1)
val tracks = Regex("""tracks:\s*\[(.+)]""").find(decrypt)?.groupValues?.get(1)
val name = url.getHost()
M3u8Helper.generateM3u8(
name,
source ?: return,
"$mainUrl/",
headers = mapOf(
"Accept" to "*/*",
"Connection" to "keep-alive",
"Sec-Fetch-Dest" to "empty",
"Sec-Fetch-Mode" to "cors",
"Sec-Fetch-Site" to "cross-site",
"Origin" to mainUrl,
)
).forEach(callback)
AppUtils.tryParseJson<List<Tracks>>("[$tracks]")
?.filter { it.kind == "captions" }?.map { track ->
subtitleCallback.invoke(
SubtitleFile(
track.label ?: "",
track.file ?: return@map null
)
)
}
}
private fun Document.getKeys(): String? {
val script = (this.selectFirst("script:containsData(eval\\()")?.data()
?.replace("eval(", "var result=")?.removeSuffix(");") + ";").trimIndent()
val run = script.runJS("result")
return """,\s*'([^']+)""".toRegex().find(run)?.groupValues?.getOrNull(1)
}
private fun String.runJS(variable: String): String {
val rhino = Context.enter()
rhino.optimizationLevel = -1
val scope: Scriptable = rhino.initSafeStandardObjects()
val result: String
try {
rhino.evaluateString(scope, this, "JavaScript", 1, null)
result = Context.toString(scope.get(variable, scope))
} finally {
Context.exit()
}
return result
}
data class Tracks(
@JsonProperty("file") val file: String? = null,
@JsonProperty("label") val label: String? = null,
@JsonProperty("kind") val kind: String? = null,
)
} }

View File

@ -20,7 +20,6 @@ open class Gomov : MainAPI() {
TvType.TvSeries, TvType.TvSeries,
TvType.AsianDrama TvType.AsianDrama
) )
private val sources = arrayOf("https://chillx.top", "https://watchx.top", "https://bestx.stream")
override val mainPage = mainPageOf( override val mainPage = mainPageOf(
"page/%d/?s&search=advanced&post_type=movie" to "Movies", "page/%d/?s&search=advanced&post_type=movie" to "Movies",
@ -46,7 +45,7 @@ open class Gomov : MainAPI() {
private fun Element.toSearchResult(): SearchResponse? { private fun Element.toSearchResult(): SearchResponse? {
val title = this.selectFirst("h2.entry-title > a")?.text()?.trim() ?: return null val title = this.selectFirst("h2.entry-title > a")?.text()?.trim() ?: return null
val href = fixUrl(this.selectFirst("a")!!.attr("href")) val href = fixUrl(this.selectFirst("a")!!.attr("href"))
val posterUrl = fixUrlNull(this.selectFirst("a > img").getImgAttr()).fixImageQuality() val posterUrl = fixUrlNull(this.selectFirst("a > img")?.getImageAttr()).fixImageQuality()
val quality = this.select("div.gmr-qual, div.gmr-quality-item > a").text().trim().replace("-", "") val quality = this.select("div.gmr-qual, div.gmr-quality-item > a").text().trim().replace("-", "")
return if (quality.isEmpty()) { return if (quality.isEmpty()) {
val episode = val episode =
@ -67,7 +66,7 @@ open class Gomov : MainAPI() {
private fun Element.toRecommendResult(): SearchResponse? { private fun Element.toRecommendResult(): SearchResponse? {
val title = this.selectFirst("a > span.idmuvi-rp-title")?.text()?.trim() ?: return null val title = this.selectFirst("a > span.idmuvi-rp-title")?.text()?.trim() ?: return null
val href = this.selectFirst("a")!!.attr("href") val href = this.selectFirst("a")!!.attr("href")
val posterUrl = fixUrlNull(this.selectFirst("a > img").getImgAttr().fixImageQuality()) val posterUrl = fixUrlNull(this.selectFirst("a > img")?.getImageAttr().fixImageQuality())
return newMovieSearchResponse(title, href, TvType.Movie) { return newMovieSearchResponse(title, href, TvType.Movie) {
this.posterUrl = posterUrl this.posterUrl = posterUrl
} }
@ -89,7 +88,7 @@ open class Gomov : MainAPI() {
document.selectFirst("h1.entry-title")?.text()?.substringBefore("Season")?.substringBefore("Episode")?.trim() document.selectFirst("h1.entry-title")?.text()?.substringBefore("Season")?.substringBefore("Episode")?.trim()
.toString() .toString()
val poster = val poster =
fixUrlNull(document.selectFirst("figure.pull-left > img").getImgAttr())?.fixImageQuality() fixUrlNull(document.selectFirst("figure.pull-left > img")?.getImageAttr())?.fixImageQuality()
val tags = document.select("span.gmr-movie-genre:contains(Genre:) > a").map { it.text() } val tags = document.select("span.gmr-movie-genre:contains(Genre:) > a").map { it.text() }
val year = val year =
@ -159,12 +158,7 @@ open class Gomov : MainAPI() {
val iframe = app.get(fixUrl(ele.attr("href"))).document.selectFirst("div.gmr-embed-responsive iframe") val iframe = app.get(fixUrl(ele.attr("href"))).document.selectFirst("div.gmr-embed-responsive iframe")
.getIframeAttr()?.let { httpsify(it) } ?: return@apmap .getIframeAttr()?.let { httpsify(it) } ?: return@apmap
when { loadExtractor(iframe, "$directUrl/", subtitleCallback, callback)
sources.any { iframe.startsWith(it) } -> NineTv.getUrl(iframe, "$directUrl/", subtitleCallback, callback)
else -> {
loadExtractor(iframe, "$directUrl/", subtitleCallback, callback)
}
}
} }
} else { } else {
document.select("div.tab-content-ajax").apmap { ele -> document.select("div.tab-content-ajax").apmap { ele ->
@ -173,12 +167,7 @@ open class Gomov : MainAPI() {
data = mapOf("action" to "muvipro_player_content", "tab" to ele.attr("id"), "post_id" to "$id") data = mapOf("action" to "muvipro_player_content", "tab" to ele.attr("id"), "post_id" to "$id")
).document.select("iframe").attr("src").let { httpsify(it) } ).document.select("iframe").attr("src").let { httpsify(it) }
when { loadExtractor(server, "$directUrl/", subtitleCallback, callback)
sources.any { server.startsWith(it) } -> NineTv.getUrl(server, "$directUrl/", subtitleCallback, callback)
else -> {
loadExtractor(server, "$directUrl/", subtitleCallback, callback)
}
}
} }
} }
@ -187,28 +176,29 @@ open class Gomov : MainAPI() {
} }
private fun Element?.getImgAttr() : String? { private fun Element.getImageAttr(): String? {
return this?.attr("data-src").takeIf { it?.isNotEmpty() == true } ?: this?.attr("src") return when {
this.hasAttr("data-src") -> this.attr("abs:data-src")
this.hasAttr("data-lazy-src") -> this.attr("abs:data-lazy-src")
this.hasAttr("srcset") -> this.attr("abs:srcset").substringBefore(" ")
else -> this.attr("abs:src")
}
} }
private fun Element?.getIframeAttr() : String? { private fun Element?.getIframeAttr() : String? {
return this?.attr("data-litespeed-src").takeIf { it?.isNotEmpty() == true } ?: this?.attr("src") return this?.attr("data-litespeed-src").takeIf { it?.isNotEmpty() == true } ?: this?.attr("src")
} }
} private fun String?.fixImageQuality(): String? {
if (this == null) return null
fun String?.fixImageQuality(): String? { val regex = Regex("(-\\d*x\\d*)").find(this)?.groupValues?.get(0) ?: return this
if (this == null) return null return this.replace(regex, "")
val regex = Regex("(-\\d*x\\d*)").find(this)?.groupValues?.get(0) ?: return this }
return this.replace(regex, "")
} private fun getBaseUrl(url: String): String {
return URI(url).let {
fun getBaseUrl(url: String): String { "${it.scheme}://${it.host}"
return URI(url).let { }
"${it.scheme}://${it.host}"
} }
}
fun String.getHost(): String {
return fixTitle(URI(this).host.substringBeforeLast(".").substringAfterLast("."))
} }

View File

@ -3,7 +3,10 @@ package com.hexated
import com.lagradost.cloudstream3.mainPageOf import com.lagradost.cloudstream3.mainPageOf
class Ngefilm : Gomov() { class Ngefilm : Gomov() {
override var mainUrl = "https://nge-film21.fun" override var mainUrl = "https://nge-film21.fun"
override var name = "Ngefilm" override var name = "Ngefilm"
override val mainPage = mainPageOf( override val mainPage = mainPageOf(

View File

@ -9,7 +9,7 @@ import org.jsoup.nodes.Element
import java.net.URI import java.net.URI
class Nodrakorid : Gomov() { class Nodrakorid : Gomov() {
override var mainUrl = "https://no-drakor.xyz" override var mainUrl = "https://nodra-kor.xyz"
override var name = "Nodrakorid" override var name = "Nodrakorid"
override val mainPage = mainPageOf( override val mainPage = mainPageOf(

View File

@ -1,5 +1,5 @@
// use an integer for version numbers // use an integer for version numbers
version = 23 version = 34
cloudstream { cloudstream {

View File

@ -1,7 +1,14 @@
package com.hexated package com.hexated
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.SubtitleFile
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.extractors.Filesim import com.lagradost.cloudstream3.extractors.Filesim
import com.lagradost.cloudstream3.extractors.StreamSB import com.lagradost.cloudstream3.extractors.StreamSB
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 Nyomo : StreamSB() { class Nyomo : StreamSB() {
override var name: String = "Nyomo" override var name: String = "Nyomo"
@ -11,4 +18,101 @@ class Nyomo : StreamSB() {
class Streamhide : Filesim() { class Streamhide : Filesim() {
override var name: String = "Streamhide" override var name: String = "Streamhide"
override var mainUrl: String = "https://streamhide.to" override var mainUrl: String = "https://streamhide.to"
}
open class Lbx : ExtractorApi() {
override val name = "Linkbox"
override val mainUrl = "https://lbx.to"
private val realUrl = "https://www.linkbox.to"
override val requiresReferer = true
override suspend fun getUrl(
url: String,
referer: String?,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
) {
val token = Regex("""(?:/f/|/file/|\?id=)(\w+)""").find(url)?.groupValues?.get(1)
val id = app.get("$realUrl/api/file/share_out_list/?sortField=utime&sortAsc=0&pageNo=1&pageSize=50&shareToken=$token").parsedSafe<Responses>()?.data?.itemId
app.get("$realUrl/api/file/detail?itemId=$id", referer = url)
.parsedSafe<Responses>()?.data?.itemInfo?.resolutionList?.map { link ->
callback.invoke(
ExtractorLink(
name,
name,
link.url ?: return@map null,
"$realUrl/",
getQualityFromName(link.resolution)
)
)
}
}
data class Resolutions(
@JsonProperty("url") val url: String? = null,
@JsonProperty("resolution") val resolution: String? = null,
)
data class ItemInfo(
@JsonProperty("resolutionList") val resolutionList: ArrayList<Resolutions>? = arrayListOf(),
)
data class Data(
@JsonProperty("itemInfo") val itemInfo: ItemInfo? = null,
@JsonProperty("itemId") val itemId: String? = null,
)
data class Responses(
@JsonProperty("data") val data: Data? = null,
)
}
open class Kuramadrive : ExtractorApi() {
override val name = "DriveKurama"
override val mainUrl = "https://kuramadrive.com"
override val requiresReferer = true
override suspend fun getUrl(
url: String,
referer: String?,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
) {
val req = app.get(url, referer = referer)
val doc = req.document
val title = doc.select("title").text()
val token = doc.select("meta[name=csrf-token]").attr("content")
val routeCheckAvl = doc.select("input#routeCheckAvl").attr("value")
val json = app.get(
routeCheckAvl, headers = mapOf(
"X-Requested-With" to "XMLHttpRequest",
"X-CSRF-TOKEN" to token
),
referer = url,
cookies = req.cookies
).parsedSafe<Source>()
callback.invoke(
ExtractorLink(
name,
name,
json?.url ?: return,
"$mainUrl/",
getIndexQuality(title),
)
)
}
private fun getIndexQuality(str: String?): Int {
return Regex("(\\d{3,4})[pP]").find(str ?: "")?.groupValues?.getOrNull(1)?.toIntOrNull()
?: Qualities.Unknown.value
}
private data class Source(
@JsonProperty("url") val url: String,
)
} }

View File

@ -8,6 +8,8 @@ import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.Qualities import com.lagradost.cloudstream3.utils.Qualities
import com.lagradost.cloudstream3.utils.loadExtractor import com.lagradost.cloudstream3.utils.loadExtractor
import com.lagradost.nicehttp.requestCreator import com.lagradost.nicehttp.requestCreator
import okhttp3.Headers
import okhttp3.HttpUrl
import org.jsoup.Jsoup import org.jsoup.Jsoup
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
@ -18,7 +20,7 @@ class KuramanimeProvider : MainAPI() {
override val hasMainPage = true override val hasMainPage = true
override var lang = "id" override var lang = "id"
override val hasDownloadSupport = true override val hasDownloadSupport = true
private var auth: String? = null private var params: AuthParams? = null
private var headers: Map<String,String> = mapOf() private var headers: Map<String,String> = mapOf()
private var cookies: Map<String,String> = mapOf() private var cookies: Map<String,String> = mapOf()
override val supportedTypes = setOf( override val supportedTypes = setOf(
@ -199,67 +201,70 @@ class KuramanimeProvider : MainAPI() {
val req = app.get(data) val req = app.get(data)
val res = req.document val res = req.document
val token = res.select("meta[name=csrf-token]").attr("content")
headers = mapOf( argamap(
"Accept" to "application/json, text/javascript, */*; q=0.01", {
"Authorization" to "${getAuth(data)}", val auth = getAuth(data)
"X-Requested-With" to "XMLHttpRequest", headers = auth.authHeader?.associate { it.first to it.second }?.filter { it.key != "Cookie" }!!
"X-CSRF-TOKEN" to token cookies = req.cookies
) res.select("select#changeServer option").apmap { source ->
cookies = req.cookies val server = source.attr("value")
res.select("select#changeServer option").apmap { source -> val query = auth.serverUrl?.queryParameterNames?.map { it } ?: return@apmap
val server = source.attr("value") val link = "$data?${query[0]}=${getMisc(auth.authUrl)}&${query[1]}=$server"
val link = "$data?dfgRr1OagZvvxbzHNpyCy0FqJQ18mCnb=${getMisc()}&twEvZlbZbYRWBdKKwxkOnwYF0VWoGGVg=$server" if (server.contains(Regex("(?i)kuramadrive|archive"))) {
if (server.contains(Regex("(?i)kuramadrive|archive"))) { invokeLocalSource(link, server, data, callback)
invokeLocalSource(link, server, data, callback) } else {
} else { app.get(
app.get( link,
link, referer = data,
referer = data, headers = headers,
headers = headers, cookies = cookies
cookies = cookies ).document.select("div.iframe-container iframe").attr("src").let { videoUrl ->
).document.select("div.iframe-container iframe").attr("src").let { videoUrl -> loadExtractor(fixUrl(videoUrl), "$mainUrl/", subtitleCallback, callback)
loadExtractor(fixUrl(videoUrl), "$mainUrl/", subtitleCallback, callback) }
}
}
},
{
res.select("div#animeDownloadLink a").apmap {
loadExtractor(it.attr("href"), "$mainUrl/", subtitleCallback, callback)
} }
} }
} )
return true return true
} }
private fun getAuth() : String { private suspend fun fetchAuth(url: String) : AuthParams {
val key = "kuramanime3:LEcXGYdOGcMCV8jM5fhRdM2mneSj6kaNts:${APIHolder.unixTimeMS};" val regex = Regex("""$mainUrl/\S+""")
return base64Encode(base64Encode(key.toByteArray()).toByteArray())
}
private suspend fun fetchAuth(url: String) : String? {
val found = WebViewResolver( val found = WebViewResolver(
Regex("$mainUrl/misc/post/EVhcpMNbO77acNZcHr2XVjaG8WAdNC1u") Regex("""$url(?!\?page=)\?"""),
additionalUrls = listOf(regex)
).resolveUsingWebView( ).resolveUsingWebView(
requestCreator( requestCreator(
"GET", url "GET", url
) )
).first )
return found?.headers?.get("Authorization") val addition = found.second.findLast { it.headers["X-Requested-With"] == "XMLHttpRequest" }
return AuthParams(found.first?.url, addition?.url.toString(), addition?.headers)
} }
private suspend fun getAuth(url: String) = auth ?: fetchAuth(url) private suspend fun getAuth(url: String) = params ?: fetchAuth(url).also { params = it }
private suspend fun getMisc(): String { private suspend fun getMisc(url: String?): String {
val misc = app.get( val misc = app.get(
"$mainUrl/misc/post/EVhcpMNbO77acNZcHr2XVjaG8WAdNC1u", "$url",
headers = headers + mapOf("X-Request-ID" to getRequestId()), headers = headers,
cookies = cookies cookies = cookies
) )
cookies = misc.cookies cookies = misc.cookies
return misc.parsed() return misc.parsed()
} }
private fun getRequestId(length: Int = 8): String { data class AuthParams (
val allowedChars = ('a'..'z') + ('0'..'9') val serverUrl: HttpUrl?,
return (1..length) val authUrl: String?,
.map { allowedChars.random() } val authHeader: Headers?,
.joinToString("") )
}
} }

View File

@ -12,5 +12,7 @@ class KuramanimeProviderPlugin: Plugin() {
registerMainAPI(KuramanimeProvider()) registerMainAPI(KuramanimeProvider())
registerExtractorAPI(Nyomo()) registerExtractorAPI(Nyomo())
registerExtractorAPI(Streamhide()) registerExtractorAPI(Streamhide())
registerExtractorAPI(Kuramadrive())
registerExtractorAPI(Lbx())
} }
} }

View File

@ -1,5 +1,5 @@
// use an integer for version numbers // use an integer for version numbers
version = 8 version = 9
cloudstream { cloudstream {

View File

@ -44,10 +44,12 @@ open class Streampai : ExtractorApi() {
this.name, this.name,
this.name, this.name,
fixUrl(it.file), fixUrl(it.file),
"$mainUrl/", url,
getQualityFromName(it.label), getQualityFromName(it.label),
headers = mapOf( headers = mapOf(
"Accept" to "video/webm,video/ogg,video/*;q=0.9,application/ogg;q=0.7,audio/*;q=0.6,*/*;q=0.5", "Accept" to "video/webm,video/ogg,video/*;q=0.9,application/ogg;q=0.7,audio/*;q=0.6,*/*;q=0.5",
"Accept-Language" to "en-US,en;q=0.5",
"DNT" to "1",
"Range" to "bytes=0-", "Range" to "bytes=0-",
"Sec-Fetch-Dest" to "video", "Sec-Fetch-Dest" to "video",
"Sec-Fetch-Mode" to "no-cors", "Sec-Fetch-Mode" to "no-cors",

View File

@ -2,10 +2,7 @@ package com.hexated
import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.network.CloudflareKiller
import com.lagradost.cloudstream3.utils.* import com.lagradost.cloudstream3.utils.*
import okhttp3.Interceptor
import okhttp3.Response
import org.jsoup.Jsoup import org.jsoup.Jsoup
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
import java.net.URLDecoder import java.net.URLDecoder
@ -17,24 +14,10 @@ class Minioppai : MainAPI() {
override var lang = "id" override var lang = "id"
override val hasDownloadSupport = true override val hasDownloadSupport = true
override val hasQuickSearch = true override val hasQuickSearch = true
private val cloudflareKiller by lazy { CloudflareKiller() }
private val interceptor by lazy { CloudflareInterceptor(cloudflareKiller) }
override val supportedTypes = setOf( override val supportedTypes = setOf(
TvType.NSFW, TvType.NSFW,
) )
class CloudflareInterceptor(private val cloudflareKiller: CloudflareKiller): Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
val response = chain.proceed(request)
val doc = Jsoup.parse(response.peekBody(1024 * 1024).string())
if (doc.select("title").text() == "Just a moment...") {
return cloudflareKiller.intercept(chain)
}
return response
}
}
companion object { companion object {
fun getStatus(t: String?): ShowStatus { fun getStatus(t: String?): ShowStatus {
return when (t) { return when (t) {
@ -54,7 +37,7 @@ class Minioppai : MainAPI() {
page: Int, page: Int,
request: MainPageRequest request: MainPageRequest
): HomePageResponse { ): HomePageResponse {
val document = app.get("${request.data}/page/$page", interceptor = interceptor).document val document = app.get("${request.data}/page/$page").document
val home = document.select("div.latest a").mapNotNull { val home = document.select("div.latest a").mapNotNull {
it.toSearchResult() it.toSearchResult()
} }
@ -84,7 +67,6 @@ class Minioppai : MainAPI() {
return newAnimeSearchResponse(title, href, TvType.NSFW) { return newAnimeSearchResponse(title, href, TvType.NSFW) {
this.posterUrl = posterUrl this.posterUrl = posterUrl
addSub(epNum) addSub(epNum)
posterHeaders = cloudflareKiller.getCookieHeaders(mainUrl).toMap()
} }
} }
@ -97,7 +79,6 @@ class Minioppai : MainAPI() {
"action" to "ts_ac_do_search", "action" to "ts_ac_do_search",
"ts_ac_query" to query, "ts_ac_query" to query,
), headers = mapOf("X-Requested-With" to "XMLHttpRequest"), ), headers = mapOf("X-Requested-With" to "XMLHttpRequest"),
interceptor = interceptor
).parsedSafe<SearchResponses>()?.post?.firstOrNull()?.all?.mapNotNull { item -> ).parsedSafe<SearchResponses>()?.post?.firstOrNull()?.all?.mapNotNull { item ->
newAnimeSearchResponse( newAnimeSearchResponse(
item.postTitle ?: "", item.postTitle ?: "",
@ -105,13 +86,12 @@ class Minioppai : MainAPI() {
TvType.NSFW TvType.NSFW
) { ) {
this.posterUrl = item.postImage this.posterUrl = item.postImage
posterHeaders = cloudflareKiller.getCookieHeaders(mainUrl).toMap()
} }
} }
} }
override suspend fun load(url: String): LoadResponse? { override suspend fun load(url: String): LoadResponse? {
val document = app.get(url, interceptor = interceptor).document val document = app.get(url).document
val title = document.selectFirst("h1.entry-title")?.text()?.trim() ?: return null val title = document.selectFirst("h1.entry-title")?.text()?.trim() ?: return null
val poster = fixUrlNull(document.selectFirst("div.limage img")?.attr("src")) val poster = fixUrlNull(document.selectFirst("div.limage img")?.attr("src"))
@ -127,7 +107,7 @@ class Minioppai : MainAPI() {
val name = it.selectFirst("div.epl-num")?.text() val name = it.selectFirst("div.epl-num")?.text()
val link = fixUrlNull(it.selectFirst("a")?.attr("href")) ?: return@mapNotNull null val link = fixUrlNull(it.selectFirst("a")?.attr("href")) ?: return@mapNotNull null
Episode(link, name = name) Episode(link, name = name)
}.reversed() }
return newAnimeLoadResponse(title, url, TvType.NSFW) { return newAnimeLoadResponse(title, url, TvType.NSFW) {
engName = title engName = title
@ -137,7 +117,6 @@ class Minioppai : MainAPI() {
showStatus = status showStatus = status
plot = description plot = description
this.tags = tags this.tags = tags
posterHeaders = cloudflareKiller.getCookieHeaders(mainUrl).toMap()
} }
} }
@ -147,7 +126,7 @@ class Minioppai : MainAPI() {
subtitleCallback: (SubtitleFile) -> Unit, subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit callback: (ExtractorLink) -> Unit
): Boolean { ): Boolean {
val document = app.get(data, interceptor = interceptor).document val document = app.get(data).document
document.select("div.server ul.mirror li a").mapNotNull { document.select("div.server ul.mirror li a").mapNotNull {
Jsoup.parse(base64Decode(it.attr("data-em"))).select("iframe").attr("src") Jsoup.parse(base64Decode(it.attr("data-em"))).select("iframe").attr("src")
}.apmap { link -> }.apmap { link ->

View File

@ -1,5 +1,5 @@
// use an integer for version numbers // use an integer for version numbers
version = 2 version = 3
cloudstream { cloudstream {
@ -23,4 +23,4 @@ cloudstream {
) )
iconUrl = "https://www.google.com/s2/favicons?domain=moviehab.com&sz=%size%" iconUrl = "https://www.google.com/s2/favicons?domain=moviehab.com&sz=%size%"
} }

View File

@ -12,7 +12,7 @@ import com.lagradost.cloudstream3.utils.loadExtractor
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
class Moviehab : MainAPI() { class Moviehab : MainAPI() {
override var mainUrl = "https://vivamax.asia" override var mainUrl = "https://nowshowing.to"
override var name = "Moviehab" override var name = "Moviehab"
override val hasMainPage = true override val hasMainPage = true
override var lang = "tl" override var lang = "tl"
@ -23,16 +23,13 @@ class Moviehab : MainAPI() {
) )
companion object { companion object {
private const val mainServer = "https://vivamax.asia" private const val mainServer = "https://nowshowing.to"
} }
override val mainPage = mainPageOf( override val mainPage = mainPageOf(
"$mainUrl/library/movies?sort_by=imdb_rate&page=" to "Movies by IMDB Rating", "$mainUrl/category/movies/" to "New Movies",
"$mainUrl/library/shows?&sort_by=imdb_rate&page=" to "TV Shows by IMDB Rating", "$mainUrl/category/tv-series/" to "New TV Shows",
"$mainUrl/library/movies?&sort_by=year&page=" to "New Movies", "$mainUrl/category/coming-soon/" to "Coming Soon",
"$mainUrl/library/shows?&sort_by=year&page=" to "New TV Shows",
"$mainUrl/library/movies?country=Philippines&sort_by=year&page=" to "New Philippines Movies",
"$mainUrl/library/shows?&country=Philippines&sort_by=year&page=" to "New Philippines TV Shows",
) )
override suspend fun getMainPage( override suspend fun getMainPage(

View File

@ -1,12 +1,12 @@
// use an integer for version numbers // use an integer for version numbers
version = 53 version = 55
cloudstream { cloudstream {
language = "hi" language = "hi"
// All of these properties are optional, you can safely remove them // All of these properties are optional, you can safely remove them
description = "Include: Hdmovie2" description = "Includes: Hdmovie2, Animesaga"
authors = listOf("Hexated") authors = listOf("Hexated")
/** /**

View File

@ -0,0 +1,23 @@
package com.hexated
import com.lagradost.cloudstream3.TvType
import com.lagradost.cloudstream3.mainPageOf
class Animesaga : Movierulzhd() {
override var mainUrl = "https://www.animesaga.in"
override var name = "Animesaga"
override val supportedTypes = setOf(
TvType.Anime,
TvType.AnimeMovie,
TvType.OVA
)
override val mainPage = mainPageOf(
"movies" to "Movies",
"tvshows" to "TV-Shows",
"genre/hindi-dub" to "Hindi Dub",
)
}

View File

@ -2,10 +2,9 @@ package com.hexated
import com.lagradost.cloudstream3.SubtitleFile import com.lagradost.cloudstream3.SubtitleFile
import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.extractors.Chillx
import com.lagradost.cloudstream3.utils.* import com.lagradost.cloudstream3.utils.*
const val twoEmbedAPI = "https://www.2embed.to"
class Sbnmp : ExtractorApi() { class Sbnmp : ExtractorApi() {
override val name = "Sbnmp" override val name = "Sbnmp"
override var mainUrl = "https://sbnmp.bar" override var mainUrl = "https://sbnmp.bar"
@ -66,3 +65,8 @@ open class Akamaicdn : ExtractorApi() {
) )
} }
} }
class AnimesagaStream : Chillx() {
override val name = "AnimesagaStream"
override val mainUrl = "https://stream.animesaga.in"
}

View File

@ -12,7 +12,7 @@ import org.jsoup.Jsoup
class Hdmovie2 : Movierulzhd() { class Hdmovie2 : Movierulzhd() {
override var mainUrl = "https://hdmovie2.boo" override var mainUrl = "https://hdmovie2.li"
override var name = "Hdmovie2" override var name = "Hdmovie2"
override val mainPage = mainPageOf( override val mainPage = mainPageOf(
"trending" to "Trending", "trending" to "Trending",
@ -44,7 +44,7 @@ class Hdmovie2 : Movierulzhd() {
} else { } else {
var document = app.get(data).document var document = app.get(data).document
if (document.select("title").text() == "Just a moment...") { if (document.select("title").text() == "Just a moment...") {
document = app.get(data, interceptor = interceptor).document document = app.get(data).document
} }
val id = document.select("meta#dooplay-ajax-counter").attr("data-postid") val id = document.select("meta#dooplay-ajax-counter").attr("data-postid")
val type = if (data.contains("/movies/")) "movie" else "tv" val type = if (data.contains("/movies/")) "movie" else "tv"

View File

@ -4,7 +4,6 @@ import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.LoadResponse.Companion.addActors import com.lagradost.cloudstream3.LoadResponse.Companion.addActors
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
import com.lagradost.cloudstream3.network.CloudflareKiller
import com.lagradost.cloudstream3.utils.* import com.lagradost.cloudstream3.utils.*
import com.lagradost.cloudstream3.utils.AppUtils.toJson import com.lagradost.cloudstream3.utils.AppUtils.toJson
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
@ -34,18 +33,13 @@ open class Movierulzhd : MainAPI() {
"episodes" to "Episode", "episodes" to "Episode",
) )
val interceptor = CloudflareKiller()
override suspend fun getMainPage( override suspend fun getMainPage(
page: Int, page: Int,
request: MainPageRequest request: MainPageRequest
): HomePageResponse { ): HomePageResponse {
var document = app.get("$mainUrl/${request.data}/page/$page").document val document = app.get("$mainUrl/${request.data}/page/$page").document
if (document.select("title").text() == "Just a moment...") {
document = app.get(request.data + page, interceptor = interceptor).document
}
val home = val home =
document.select("div.items.normal article, div#archive-content article").mapNotNull { document.select("div.items.normal article, div#archive-content article, div.items.full article").mapNotNull {
it.toSearchResult() it.toSearchResult()
} }
return newHomePageResponse(request.name, home) return newHomePageResponse(request.name, home)
@ -74,23 +68,17 @@ open class Movierulzhd : MainAPI() {
private fun Element.toSearchResult(): SearchResponse? { private fun Element.toSearchResult(): SearchResponse? {
val title = this.selectFirst("h3 > a")?.text() ?: return null val title = this.selectFirst("h3 > a")?.text() ?: return null
val href = getProperLink(fixUrl(this.selectFirst("h3 > a")!!.attr("href"))) val href = getProperLink(fixUrl(this.selectFirst("h3 > a")!!.attr("href")))
val posterUrl = fixUrlNull(this.select("div.poster img").last()?.attr("src")) val posterUrl = fixUrlNull(this.select("div.poster img").last()?.getImageAttr())
val quality = getQualityFromString(this.select("span.quality").text()) val quality = getQualityFromString(this.select("span.quality").text())
return newMovieSearchResponse(title, href, TvType.Movie) { return newMovieSearchResponse(title, href, TvType.Movie) {
this.posterUrl = posterUrl this.posterUrl = posterUrl
this.quality = quality this.quality = quality
posterHeaders = interceptor.getCookieHeaders(mainUrl).toMap()
} }
} }
override suspend fun search(query: String): List<SearchResponse> { override suspend fun search(query: String): List<SearchResponse> {
val link = "$mainUrl/search/$query" val document = app.get("$mainUrl/search/$query").document
var document = app.get(link).document
if (document.select("title").text() == "Just a moment...") {
document = app.get(link, interceptor = interceptor).document
}
return document.select("div.result-item").map { return document.select("div.result-item").map {
val title = val title =
it.selectFirst("div.title > a")!!.text().replace(Regex("\\(\\d{4}\\)"), "").trim() it.selectFirst("div.title > a")!!.text().replace(Regex("\\(\\d{4}\\)"), "").trim()
@ -98,21 +86,17 @@ open class Movierulzhd : MainAPI() {
val posterUrl = it.selectFirst("img")!!.attr("src").toString() val posterUrl = it.selectFirst("img")!!.attr("src").toString()
newMovieSearchResponse(title, href, TvType.TvSeries) { newMovieSearchResponse(title, href, TvType.TvSeries) {
this.posterUrl = posterUrl this.posterUrl = posterUrl
posterHeaders = interceptor.getCookieHeaders(mainUrl).toMap()
} }
} }
} }
override suspend fun load(url: String): LoadResponse { override suspend fun load(url: String): LoadResponse {
val request = app.get(url) val request = app.get(url)
var document = request.document val document = request.document
if (document.select("title").text() == "Just a moment...") {
document = app.get(url, interceptor = interceptor).document
}
directUrl = getBaseUrl(request.url) directUrl = getBaseUrl(request.url)
val title = val title =
document.selectFirst("div.data > h1")?.text()?.trim().toString() document.selectFirst("div.data > h1")?.text()?.trim().toString()
val poster = fixUrlNull(document.select("div.poster img:last-child").attr("src")) val poster = fixUrlNull(document.selectFirst("div.poster img:last-child")?.getImageAttr())
val tags = document.select("div.sgeneros > a").map { it.text() } val tags = document.select("div.sgeneros > a").map { it.text() }
val year = Regex(",\\s?(\\d+)").find( val year = Regex(",\\s?(\\d+)").find(
@ -139,10 +123,9 @@ open class Movierulzhd : MainAPI() {
val recName = val recName =
it.selectFirst("a")!!.attr("href").toString().removeSuffix("/").split("/").last() it.selectFirst("a")!!.attr("href").toString().removeSuffix("/").split("/").last()
val recHref = it.selectFirst("a")!!.attr("href") val recHref = it.selectFirst("a")!!.attr("href")
val recPosterUrl = it.selectFirst("img")?.attr("src").toString() val recPosterUrl = it.selectFirst("img")?.getImageAttr()
newTvSeriesSearchResponse(recName, recHref, TvType.TvSeries) { newTvSeriesSearchResponse(recName, recHref, TvType.TvSeries) {
this.posterUrl = recPosterUrl this.posterUrl = recPosterUrl
posterHeaders = interceptor.getCookieHeaders(url).toMap()
} }
} }
@ -151,7 +134,7 @@ open class Movierulzhd : MainAPI() {
document.select("ul.episodios > li").map { document.select("ul.episodios > li").map {
val href = it.select("a").attr("href") val href = it.select("a").attr("href")
val name = fixTitle(it.select("div.episodiotitle > a").text().trim()) val name = fixTitle(it.select("div.episodiotitle > a").text().trim())
val image = it.select("div.imagen > img").attr("src") val image = it.selectFirst("div.imagen > img")?.getImageAttr()
val episode = val episode =
it.select("div.numerando").text().replace(" ", "").split("-").last() it.select("div.numerando").text().replace(" ", "").split("-").last()
.toIntOrNull() .toIntOrNull()
@ -187,7 +170,6 @@ open class Movierulzhd : MainAPI() {
addActors(actors) addActors(actors)
this.recommendations = recommendations this.recommendations = recommendations
addTrailer(trailer) addTrailer(trailer)
posterHeaders = interceptor.getCookieHeaders(url).toMap()
} }
} else { } else {
newMovieLoadResponse(title, url, TvType.Movie, url) { newMovieLoadResponse(title, url, TvType.Movie, url) {
@ -199,7 +181,6 @@ open class Movierulzhd : MainAPI() {
addActors(actors) addActors(actors)
this.recommendations = recommendations this.recommendations = recommendations
addTrailer(trailer) addTrailer(trailer)
posterHeaders = interceptor.getCookieHeaders(url).toMap()
} }
} }
} }
@ -230,18 +211,17 @@ open class Movierulzhd : MainAPI() {
referer = data, referer = data,
headers = mapOf("X-Requested-With" to "XMLHttpRequest") headers = mapOf("X-Requested-With" to "XMLHttpRequest")
).parsed<ResponseHash>().embed_url ).parsed<ResponseHash>().embed_url
if (!source.contains("youtube")) loadCustomExtractor(loadData?.tag, source, "$directUrl/", subtitleCallback, callback) if (!source.contains("youtube")) loadCustomExtractor(source, "$directUrl/", subtitleCallback, callback)
} else { } else {
var document = app.get(data).document val document = app.get(data).document
if (document.select("title").text() == "Just a moment...") {
document = app.get(data, interceptor = interceptor).document
}
val id = document.select("meta#dooplay-ajax-counter").attr("data-postid")
val type = if (data.contains("/movies/")) "movie" else "tv"
document.select("ul#playeroptionsul > li").map { document.select("ul#playeroptionsul > li").map {
it.attr("data-nume") to it.select("span.title").text() Triple(
}.apmap { (nume, tag) -> it.attr("data-post"),
it.attr("data-nume"),
it.attr("data-type")
)
}.apmap { (id, nume, type) ->
val source = app.post( val source = app.post(
url = "$directUrl/wp-admin/admin-ajax.php", url = "$directUrl/wp-admin/admin-ajax.php",
data = mapOf( data = mapOf(
@ -256,7 +236,6 @@ open class Movierulzhd : MainAPI() {
when { when {
!source.contains("youtube") -> loadCustomExtractor( !source.contains("youtube") -> loadCustomExtractor(
tag,
source, source,
"$directUrl/", "$directUrl/",
subtitleCallback, subtitleCallback,
@ -269,8 +248,16 @@ open class Movierulzhd : MainAPI() {
return true return true
} }
private fun Element.getImageAttr(): String? {
return when {
this.hasAttr("data-src") -> this.attr("abs:data-src")
this.hasAttr("data-lazy-src") -> this.attr("abs:data-lazy-src")
this.hasAttr("srcset") -> this.attr("abs:srcset").substringBefore(" ")
else -> this.attr("abs:src")
}
}
private suspend fun loadCustomExtractor( private suspend fun loadCustomExtractor(
name: String? = null,
url: String, url: String,
referer: String? = null, referer: String? = null,
subtitleCallback: (SubtitleFile) -> Unit, subtitleCallback: (SubtitleFile) -> Unit,
@ -278,21 +265,23 @@ open class Movierulzhd : MainAPI() {
quality: Int? = null, quality: Int? = null,
) { ) {
loadExtractor(url, referer, subtitleCallback) { link -> loadExtractor(url, referer, subtitleCallback) { link ->
callback.invoke( if(link.quality == Qualities.Unknown.value) {
ExtractorLink( callback.invoke(
name ?: link.source, ExtractorLink(
name ?: link.name, link.source,
link.url, link.name,
link.referer, link.url,
when (link.type) { link.referer,
ExtractorLinkType.M3U8 -> link.quality when (link.type) {
else -> quality ?: link.quality ExtractorLinkType.M3U8 -> link.quality
}, else -> quality ?: link.quality
link.type, },
link.headers, link.type,
link.extractorData link.headers,
link.extractorData
)
) )
) }
} }
} }

View File

@ -11,7 +11,9 @@ class MovierulzhdPlugin: Plugin() {
// All providers should be added in this manner. Please don't edit the providers list directly. // All providers should be added in this manner. Please don't edit the providers list directly.
registerMainAPI(Movierulzhd()) registerMainAPI(Movierulzhd())
registerMainAPI(Hdmovie2()) registerMainAPI(Hdmovie2())
registerMainAPI(Animesaga())
registerExtractorAPI(Sbnmp()) registerExtractorAPI(Sbnmp())
registerExtractorAPI(Akamaicdn()) registerExtractorAPI(Akamaicdn())
registerExtractorAPI(AnimesagaStream())
} }
} }

View File

@ -4,13 +4,9 @@ import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.LoadResponse.Companion.addAniListId import com.lagradost.cloudstream3.LoadResponse.Companion.addAniListId
import com.lagradost.cloudstream3.LoadResponse.Companion.addMalId import com.lagradost.cloudstream3.LoadResponse.Companion.addMalId
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
import com.lagradost.cloudstream3.network.CloudflareKiller
import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.Qualities import com.lagradost.cloudstream3.utils.Qualities
import com.lagradost.cloudstream3.utils.loadExtractor import com.lagradost.cloudstream3.utils.loadExtractor
import okhttp3.Interceptor
import okhttp3.Response
import org.jsoup.Jsoup
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
class Samehadaku : MainAPI() { class Samehadaku : MainAPI() {
@ -19,26 +15,12 @@ class Samehadaku : MainAPI() {
override val hasMainPage = true override val hasMainPage = true
override var lang = "id" override var lang = "id"
override val hasDownloadSupport = true override val hasDownloadSupport = true
private val cloudflareKiller by lazy { CloudflareKiller() }
private val interceptor by lazy { CloudflareInterceptor(cloudflareKiller) }
override val supportedTypes = setOf( override val supportedTypes = setOf(
TvType.Anime, TvType.Anime,
TvType.AnimeMovie, TvType.AnimeMovie,
TvType.OVA TvType.OVA
) )
class CloudflareInterceptor(private val cloudflareKiller: CloudflareKiller): Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
val response = chain.proceed(request)
val doc = Jsoup.parse(response.peekBody(1024 * 1024).string())
if (doc.select("title").text() == "Just a moment...") {
return cloudflareKiller.intercept(chain)
}
return response
}
}
companion object { companion object {
const val acefile = "https://acefile.co" const val acefile = "https://acefile.co"
@ -69,7 +51,7 @@ class Samehadaku : MainAPI() {
val items = mutableListOf<HomePageList>() val items = mutableListOf<HomePageList>()
if (request.name != "Episode Terbaru" && page <= 1) { if (request.name != "Episode Terbaru" && page <= 1) {
val doc = app.get(request.data, interceptor = interceptor).document val doc = app.get(request.data).document
doc.select("div.widget_senction:not(:contains(Baca Komik))").forEach { block -> doc.select("div.widget_senction:not(:contains(Baca Komik))").forEach { block ->
val header = block.selectFirst("div.widget-title h3")?.ownText() ?: return@forEach val header = block.selectFirst("div.widget-title h3")?.ownText() ?: return@forEach
val home = block.select("div.animepost").mapNotNull { val home = block.select("div.animepost").mapNotNull {
@ -80,8 +62,7 @@ class Samehadaku : MainAPI() {
} }
if (request.name == "Episode Terbaru") { if (request.name == "Episode Terbaru") {
val home = val home = app.get(request.data + page).document.selectFirst("div.post-show")?.select("ul li")
app.get(request.data + page, interceptor = interceptor).document.selectFirst("div.post-show")?.select("ul li")
?.mapNotNull { ?.mapNotNull {
it.toSearchResult() it.toSearchResult()
} ?: throw ErrorLoadingException("No Media Found") } ?: throw ErrorLoadingException("No Media Found")
@ -101,13 +82,12 @@ class Samehadaku : MainAPI() {
return newAnimeSearchResponse(title, href ?: return null, TvType.Anime) { return newAnimeSearchResponse(title, href ?: return null, TvType.Anime) {
this.posterUrl = posterUrl this.posterUrl = posterUrl
addSub(epNum) addSub(epNum)
posterHeaders = cloudflareKiller.getCookieHeaders(mainUrl).toMap()
} }
} }
override suspend fun search(query: String): List<SearchResponse> { override suspend fun search(query: String): List<SearchResponse> {
val document = app.get("$mainUrl/?s=$query", interceptor = interceptor).document val document = app.get("$mainUrl/?s=$query").document
return document.select("main#main div.animepost").mapNotNull { return document.select("main#main div.animepost").mapNotNull {
it.toSearchResult() it.toSearchResult()
} }
@ -117,10 +97,10 @@ class Samehadaku : MainAPI() {
val fixUrl = if (url.contains("/anime/")) { val fixUrl = if (url.contains("/anime/")) {
url url
} else { } else {
app.get(url, interceptor = interceptor).document.selectFirst("div.nvs.nvsc a")?.attr("href") app.get(url).document.selectFirst("div.nvs.nvsc a")?.attr("href")
} }
val document = app.get(fixUrl ?: return null, interceptor = interceptor).document val document = app.get(fixUrl ?: return null).document
val title = document.selectFirst("h1.entry-title")?.text()?.removeBloat() ?: return null val title = document.selectFirst("h1.entry-title")?.text()?.removeBloat() ?: return null
val poster = document.selectFirst("div.thumb > img")?.attr("src") val poster = document.selectFirst("div.thumb > img")?.attr("src")
val tags = document.select("div.genre-info > a").map { it.text() } val tags = document.select("div.genre-info > a").map { it.text() }
@ -165,7 +145,6 @@ class Samehadaku : MainAPI() {
this.recommendations = recommendations this.recommendations = recommendations
addMalId(tracker?.malId) addMalId(tracker?.malId)
addAniListId(tracker?.aniId?.toIntOrNull()) addAniListId(tracker?.aniId?.toIntOrNull())
posterHeaders = cloudflareKiller.getCookieHeaders(mainUrl).toMap()
} }
} }
@ -177,7 +156,7 @@ class Samehadaku : MainAPI() {
callback: (ExtractorLink) -> Unit callback: (ExtractorLink) -> Unit
): Boolean { ): Boolean {
val document = app.get(data, interceptor = interceptor).document val document = app.get(data).document
argamap( argamap(
{ {
@ -196,7 +175,6 @@ class Samehadaku : MainAPI() {
), ),
referer = data, referer = data,
headers = mapOf("X-Requested-With" to "XMLHttpRequest"), headers = mapOf("X-Requested-With" to "XMLHttpRequest"),
interceptor = interceptor
).document.select("iframe").attr("src") ).document.select("iframe").attr("src")
loadFixedExtractor(fixedIframe(iframe), it.text(), "$mainUrl/", subtitleCallback, callback) loadFixedExtractor(fixedIframe(iframe), it.text(), "$mainUrl/", subtitleCallback, callback)

View File

@ -1,7 +1,7 @@
import org.jetbrains.kotlin.konan.properties.Properties import org.jetbrains.kotlin.konan.properties.Properties
// use an integer for version numbers // use an integer for version numbers
version = 197 version = 204
android { android {
defaultConfig { defaultConfig {
@ -9,15 +9,12 @@ android {
properties.load(project.rootProject.file("local.properties").inputStream()) properties.load(project.rootProject.file("local.properties").inputStream())
buildConfigField("String", "TMDB_API", "\"${properties.getProperty("TMDB_API")}\"") buildConfigField("String", "TMDB_API", "\"${properties.getProperty("TMDB_API")}\"")
buildConfigField("String", "OMOVIES_API", "\"${properties.getProperty("OMOVIES_API")}\"")
buildConfigField("String", "CINEMATV_API", "\"${properties.getProperty("CINEMATV_API")}\"")
buildConfigField("String", "SFMOVIES_API", "\"${properties.getProperty("SFMOVIES_API")}\"") buildConfigField("String", "SFMOVIES_API", "\"${properties.getProperty("SFMOVIES_API")}\"")
buildConfigField("String", "ZSHOW_API", "\"${properties.getProperty("ZSHOW_API")}\"") buildConfigField("String", "ZSHOW_API", "\"${properties.getProperty("ZSHOW_API")}\"")
buildConfigField("String", "SORA_API", "\"${properties.getProperty("SORA_API")}\"")
buildConfigField("String", "SORAHE", "\"${properties.getProperty("SORAHE")}\"")
buildConfigField("String", "SORAXA", "\"${properties.getProperty("SORAXA")}\"")
buildConfigField("String", "SORATED", "\"${properties.getProperty("SORATED")}\"")
buildConfigField("String", "DUMP_API", "\"${properties.getProperty("DUMP_API")}\"") buildConfigField("String", "DUMP_API", "\"${properties.getProperty("DUMP_API")}\"")
buildConfigField("String", "DUMP_KEY", "\"${properties.getProperty("DUMP_KEY")}\"") buildConfigField("String", "DUMP_KEY", "\"${properties.getProperty("DUMP_KEY")}\"")
buildConfigField("String", "PRIMEWIRE_KEY", "\"${properties.getProperty("PRIMEWIRE_KEY")}\"")
buildConfigField("String", "CRUNCHYROLL_BASIC_TOKEN", "\"${properties.getProperty("CRUNCHYROLL_BASIC_TOKEN")}\"") buildConfigField("String", "CRUNCHYROLL_BASIC_TOKEN", "\"${properties.getProperty("CRUNCHYROLL_BASIC_TOKEN")}\"")
buildConfigField("String", "CRUNCHYROLL_REFRESH_TOKEN", "\"${properties.getProperty("CRUNCHYROLL_REFRESH_TOKEN")}\"") buildConfigField("String", "CRUNCHYROLL_REFRESH_TOKEN", "\"${properties.getProperty("CRUNCHYROLL_REFRESH_TOKEN")}\"")
} }

View File

@ -6,17 +6,13 @@ import com.lagradost.cloudstream3.extractors.StreamSB
import com.lagradost.cloudstream3.extractors.Voe import com.lagradost.cloudstream3.extractors.Voe
import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.APIHolder.getCaptchaToken import com.lagradost.cloudstream3.APIHolder.getCaptchaToken
import com.lagradost.cloudstream3.ErrorLoadingException
import com.lagradost.cloudstream3.SubtitleFile import com.lagradost.cloudstream3.SubtitleFile
import com.lagradost.cloudstream3.apmap import com.lagradost.cloudstream3.apmap
import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.base64Decode import com.lagradost.cloudstream3.base64Decode
import com.lagradost.cloudstream3.extractors.Pixeldrain import com.lagradost.cloudstream3.extractors.Pixeldrain
import com.lagradost.cloudstream3.extractors.helper.AesHelper import com.lagradost.cloudstream3.extractors.Vidplay
import com.lagradost.cloudstream3.utils.* import com.lagradost.cloudstream3.utils.*
import org.jsoup.nodes.Document
import org.mozilla.javascript.Context
import org.mozilla.javascript.Scriptable
import java.math.BigInteger import java.math.BigInteger
import java.security.MessageDigest import java.security.MessageDigest
@ -202,9 +198,10 @@ open class VCloud : ExtractorApi() {
val res = app.get(url) val res = app.get(url)
val doc = res.document val doc = res.document
val changedLink = doc.selectFirst("script:containsData(url =)")?.data()?.let { val changedLink = doc.selectFirst("script:containsData(url =)")?.data()?.let {
"""url\s*=\s*['"](.*)['"];""".toRegex().find(it)?.groupValues?.get(1) val regex = """url\s*=\s*['"](.*)['"];""".toRegex()
?.substringAfter("r=") val doc2 = app.get(regex.find(it)?.groupValues?.get(1) ?: return).text
} ?: doc.selectFirst("div.div.vd.d-none a")?.attr("href") regex.find(doc2)?.groupValues?.get(1)?.substringAfter("r=")
}
val header = doc.selectFirst("div.card-header")?.text() val header = doc.selectFirst("div.card-header")?.text()
app.get( app.get(
base64Decode(changedLink ?: return), cookies = res.cookies, headers = mapOf( base64Decode(changedLink ?: return), cookies = res.cookies, headers = mapOf(
@ -212,7 +209,7 @@ open class VCloud : ExtractorApi() {
) )
).document.select("p.text-success ~ a").apmap { ).document.select("p.text-success ~ a").apmap {
val link = it.attr("href") val link = it.attr("href")
if (link.contains("workers.dev")) { if (link.contains("workers.dev") || it.text().contains("[Server : 1]") || link.contains("/dl.php?")) {
callback.invoke( callback.invoke(
ExtractorLink( ExtractorLink(
this.name, this.name,
@ -238,83 +235,6 @@ open class VCloud : ExtractorApi() {
} }
object NineTv {
suspend fun getUrl(
url: String,
referer: String?,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
) {
val mainUrl = getBaseUrl(url)
val res = app.get(url, headers = mapOf(
"Accept" to "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
"Accept-Language" to "en-US,en;q=0.5",
"Referer" to (referer ?: ""),
))
val master = Regex("\\s*=\\s*'([^']+)").find(res.text)?.groupValues?.get(1)
val key = "tSIsE8FgpRkv3QQQ"
val decrypt = AesHelper.cryptoAESHandler(master ?: return, key.toByteArray(), false)
?.replace("\\", "")
?: throw ErrorLoadingException("failed to decrypt")
val source = Regex(""""?file"?:\s*"([^"]+)""").find(decrypt)?.groupValues?.get(1)
val tracks = Regex("""tracks:\s*\[(.+)]""").find(decrypt)?.groupValues?.get(1)
val name = url.getHost()
M3u8Helper.generateM3u8(
name,
source ?: return,
"$mainUrl/",
headers = mapOf(
"Accept" to "*/*",
"Connection" to "keep-alive",
"Sec-Fetch-Dest" to "empty",
"Sec-Fetch-Mode" to "cors",
"Sec-Fetch-Site" to "cross-site",
"Origin" to mainUrl,
)
).forEach(callback)
AppUtils.tryParseJson<List<Tracks>>("[$tracks]")
?.filter { it.kind == "captions" }?.map { track ->
subtitleCallback.invoke(
SubtitleFile(
track.label ?: "",
track.file ?: return@map null
)
)
}
}
private fun Document.getKeys(): String? {
val script = (this.selectFirst("script:containsData(eval\\()")?.data()
?.replace("eval(", "var result=")?.removeSuffix(");") + ";").trimIndent()
val run = script.runJS("result")
return """,\s*'([^']+)""".toRegex().find(run)?.groupValues?.getOrNull(1)
}
private fun String.runJS(variable: String): String {
val rhino = Context.enter()
rhino.optimizationLevel = -1
val scope: Scriptable = rhino.initSafeStandardObjects()
val result: String
try {
rhino.evaluateString(scope, this, "JavaScript", 1, null)
result = Context.toString(scope.get(variable, scope))
} finally {
Context.exit()
}
return result
}
data class Tracks(
@JsonProperty("file") val file: String? = null,
@JsonProperty("label") val label: String? = null,
@JsonProperty("kind") val kind: String? = null,
)
}
open class Streamruby : ExtractorApi() { open class Streamruby : ExtractorApi() {
override val name = "Streamruby" override val name = "Streamruby"
override val mainUrl = "https://streamruby.com" override val mainUrl = "https://streamruby.com"
@ -390,6 +310,40 @@ open class Uploadever : ExtractorApi() {
} }
open class Netembed : ExtractorApi() {
override var name: String = "Netembed"
override var mainUrl: String = "https://play.netembed.xyz"
override val requiresReferer = true
override suspend fun getUrl(
url: String,
referer: String?,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
) {
val response = app.get(url, referer = referer)
val script = getAndUnpack(response.text)
val m3u8 = Regex("((https:|http:)//.*\\.m3u8)").find(script)?.groupValues?.getOrNull(1)
callback.invoke(
ExtractorLink(
this.name,
this.name,
m3u8 ?: return,
"$mainUrl/",
getQuality(m3u8),
INFER_TYPE
)
)
}
private suspend fun getQuality(url: String) : Int {
val res = app.get(url, referer = "$mainUrl/").text
val regex = "#quality:\\s*(\\S+)".toRegex().find(res)?.groupValues?.get(1)
return getQualityFromName(regex)
}
}
class Streamwish : Filesim() { class Streamwish : Filesim() {
override val name = "Streamwish" override val name = "Streamwish"
override var mainUrl = "https://streamwish.to" override var mainUrl = "https://streamwish.to"
@ -405,11 +359,6 @@ class FilelionsTo : Filesim() {
override var mainUrl = "https://filelions.to" override var mainUrl = "https://filelions.to"
} }
class Hubcloud : VCloud() {
override val name = "Hubcloud"
override val mainUrl = "https://hubcloud.in"
}
class Pixeldra : Pixeldrain() { class Pixeldra : Pixeldrain() {
override val mainUrl = "https://pixeldra.in" override val mainUrl = "https://pixeldra.in"
} }
@ -447,4 +396,8 @@ class Yipsu : Voe() {
class Embedwish : Filesim() { class Embedwish : Filesim() {
override val name = "Embedwish" override val name = "Embedwish"
override var mainUrl = "https://embedwish.com" override var mainUrl = "https://embedwish.com"
}
class Vidplay2 : Vidplay() {
override val mainUrl = "https://vidplay.online"
} }

View File

@ -2,6 +2,7 @@ package com.hexated
import com.hexated.AESGCM.decrypt import com.hexated.AESGCM.decrypt
import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.APIHolder.unixTime
import com.lagradost.cloudstream3.APIHolder.unixTimeMS import com.lagradost.cloudstream3.APIHolder.unixTimeMS
import com.lagradost.cloudstream3.utils.* import com.lagradost.cloudstream3.utils.*
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
@ -110,7 +111,8 @@ object SoraExtractor : SoraStream() {
"$vidSrcAPI/embed/tv?tmdb=$id&season=$season&episode=$episode" "$vidSrcAPI/embed/tv?tmdb=$id&season=$season&episode=$episode"
} }
val iframedoc = app.get(url).document.select("iframe#player_iframe").attr("src").let { httpsify(it) } val iframedoc =
app.get(url).document.select("iframe#player_iframe").attr("src").let { httpsify(it) }
val doc = app.get(iframedoc, referer = url).document val doc = app.get(iframedoc, referer = url).document
val index = doc.select("body").attr("data-i") val index = doc.select("body").attr("data-i")
@ -246,6 +248,7 @@ object SoraExtractor : SoraStream() {
} else { } else {
"$multimoviesAPI/episodes/$fixTitle-${season}x${episode}" "$multimoviesAPI/episodes/$fixTitle-${season}x${episode}"
} }
invokeWpmovies(null, url, subtitleCallback, callback)
} }
suspend fun invokeNetmovies( suspend fun invokeNetmovies(
@ -273,12 +276,11 @@ object SoraExtractor : SoraStream() {
subtitleCallback: (SubtitleFile) -> Unit, subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit callback: (ExtractorLink) -> Unit
) { ) {
val api = BuildConfig.ZSHOW_API
val fixTitle = title.createSlug() val fixTitle = title.createSlug()
val url = if (season == null) { val url = if (season == null) {
"$api/movie/$fixTitle-$year" "$zshowAPI/movie/$fixTitle-$year"
} else { } else {
"$api/episode/$fixTitle-season-$season-episode-$episode" "$zshowAPI/episode/$fixTitle-season-$season-episode-$episode"
} }
invokeWpmovies("ZShow", url, subtitleCallback, callback, encrypt = true) invokeWpmovies("ZShow", url, subtitleCallback, callback, encrypt = true)
} }
@ -326,20 +328,7 @@ object SoraExtractor : SoraStream() {
else -> it.embed_url else -> it.embed_url
} }
} ?: return@apmap } ?: return@apmap
val sources = arrayOf(
"https://chillx.top",
"https://watchx.top",
"https://bestx.stream",
"https://w1.moviesapi.club"
)
when { when {
sources.any { source.startsWith(it) } -> NineTv.getUrl(
source,
"$referer/",
subtitleCallback,
callback
)
!source.contains("youtube") -> { !source.contains("youtube") -> {
loadCustomExtractor(name, source, "$referer/", subtitleCallback, callback) loadCustomExtractor(name, source, "$referer/", subtitleCallback, callback)
} }
@ -451,7 +440,7 @@ object SoraExtractor : SoraStream() {
sourcesData?.get("movie")?.get("movie") sourcesData?.get("movie")?.get("movie")
} else { } else {
sourcesData?.get("s$seasonSlug")?.get("e$episodeSlug") sourcesData?.get("s$seasonSlug")?.get("e$episodeSlug")
} } ?: return
val subSources = if (season == null) { val subSources = if (season == null) {
subSourcesData?.get("movie")?.get("movie") subSourcesData?.get("movie")?.get("movie")
} else { } else {
@ -464,39 +453,45 @@ object SoraExtractor : SoraStream() {
Regex("var\\suserNonce.*?[\"|'](\\S+?)[\"|'];").find(scriptUser)?.groupValues?.get(1) Regex("var\\suserNonce.*?[\"|'](\\S+?)[\"|'];").find(scriptUser)?.groupValues?.get(1)
val userId = val userId =
Regex("var\\suser_id.*?[\"|'](\\S+?)[\"|'];").find(scriptUser)?.groupValues?.get(1) Regex("var\\suser_id.*?[\"|'](\\S+?)[\"|'];").find(scriptUser)?.groupValues?.get(1)
val linkIDs = sources?.joinToString("") {
"&linkIDs%5B%5D=$it"
}?.replace("\"", "")
val json = app.post( val listSources = sources.withIndex()
"$filmxyAPI/wp-admin/admin-ajax.php", .groupBy { it.index / 2 }
requestBody = "action=get_vid_links$linkIDs&user_id=$userId&nonce=$userNonce".toRequestBody(), .map { entry -> entry.value.map { it.value } }
referer = url,
headers = mapOf(
"Accept" to "*/*",
"DNT" to "1",
"Content-Type" to "application/x-www-form-urlencoded; charset=UTF-8",
"Origin" to filmxyAPI,
"X-Requested-With" to "XMLHttpRequest",
),
cookies = filmxyCookies
).text.let { tryParseJson<HashMap<String, String>>(it) }
sources?.map { source -> listSources.apmap { src ->
val link = json?.get(source) val linkIDs = src.joinToString("") {
val quality = sourcesDetail?.get(source)?.get("resolution") "&linkIDs%5B%5D=$it"
val server = sourcesDetail?.get(source)?.get("server") }.replace("\"", "")
val size = sourcesDetail?.get(source)?.get("size") val json = app.post(
"$filmxyAPI/wp-admin/admin-ajax.php",
requestBody = "action=get_vid_links$linkIDs&user_id=$userId&nonce=$userNonce".toRequestBody(),
referer = url,
headers = mapOf(
"Accept" to "*/*",
"DNT" to "1",
"Content-Type" to "application/x-www-form-urlencoded; charset=UTF-8",
"Origin" to filmxyAPI,
"X-Requested-With" to "XMLHttpRequest",
),
cookies = filmxyCookies
).text.let { tryParseJson<HashMap<String, String>>(it) }
callback.invoke( src.map { source ->
ExtractorLink( val link = json?.get(source)
"Filmxy", val quality = sourcesDetail?.get(source)?.get("resolution")
"Filmxy $server [$size]", val server = sourcesDetail?.get(source)?.get("server")
link ?: return@map, val size = sourcesDetail?.get(source)?.get("size")
"$filmxyAPI/",
getQualityFromName(quality) callback.invoke(
ExtractorLink(
"Filmxy",
"Filmxy $server [$size]",
link ?: return@map,
"$filmxyAPI/",
getQualityFromName(quality)
)
) )
) }
} }
subSources?.mapKeys { sub -> subSources?.mapKeys { sub ->
@ -686,12 +681,20 @@ object SoraExtractor : SoraStream() {
"$vidsrctoAPI/embed/tv/$imdbId/$season/$episode" "$vidsrctoAPI/embed/tv/$imdbId/$season/$episode"
} }
val mediaId = app.get(url).document.selectFirst("ul.episodes li a")?.attr("data-id") ?: return val mediaId =
app.get(url).document.selectFirst("ul.episodes li a")?.attr("data-id") ?: return
app.get("$vidsrctoAPI/ajax/embed/episode/$mediaId/sources").parsedSafe<VidsrctoSources>()?.result?.apmap { app.get("$vidsrctoAPI/ajax/embed/episode/$mediaId/sources")
val encUrl = app.get("$vidsrctoAPI/ajax/embed/source/${it.id}").parsedSafe<VidsrctoResponse>()?.result?.url .parsedSafe<VidsrctoSources>()?.result?.apmap {
loadExtractor(vidsrctoDecrypt(encUrl ?: return@apmap), "$vidsrctoAPI/", subtitleCallback, callback) val encUrl = app.get("$vidsrctoAPI/ajax/embed/source/${it.id}")
} .parsedSafe<VidsrctoResponse>()?.result?.url
loadExtractor(
vidsrctoDecrypt(encUrl ?: return@apmap),
"$vidsrctoAPI/",
subtitleCallback,
callback
)
}
val subtitles = app.get("$vidsrctoAPI/ajax/embed/episode/$mediaId/subtitles").text val subtitles = app.get("$vidsrctoAPI/ajax/embed/episode/$mediaId/subtitles").text
tryParseJson<List<VidsrctoSubtitles>>(subtitles)?.map { tryParseJson<List<VidsrctoSubtitles>>(subtitles)?.map {
@ -965,62 +968,50 @@ object SoraExtractor : SoraStream() {
title: String? = null, title: String? = null,
year: Int? = null, year: Int? = null,
season: Int? = null, season: Int? = null,
lastSeason: Int? = null,
episode: Int? = null, episode: Int? = null,
callback: (ExtractorLink) -> Unit callback: (ExtractorLink) -> Unit
) { ) {
val slug = title.createSlug()?.replace("-", " ") val fixTitle = title.createSlug()
val url = "$uhdmoviesAPI/?s=$slug" val (seasonSlug, episodeSlug) = getEpisodeSlug(season, episode)
var doc = app.get(url).document
if (doc.select("title").text() == "Just a moment...") {
doc = app.get(url, interceptor = CloudflareKiller()).document
}
val scriptData = doc.select("div.row.gridlove-posts article").map {
it.selectFirst("a")?.attr("href") to it.selectFirst("h1")?.text()
}
val detailUrl = (if (scriptData.size == 1) { val url = if(season == null) {
scriptData.first() "$uhdmoviesAPI/download-$fixTitle-$year"
} else { } else {
scriptData.find { it.second?.filterMedia(title, year, lastSeason) == true } "$uhdmoviesAPI/download-$fixTitle"
})?.first }
val detailDoc = app.get(detailUrl ?: return).document val detailDoc = app.get(url).document
val iframeList = detailDoc.select("div.entry-content p").map { it } val iSelector = if(season == null) {
.filter { it.text().filterIframe(season, lastSeason, year, title) }.mapNotNull { "div.entry-content p:has(:matches($year))"
if (season == null) { } else {
it.text() to it.nextElementSibling()?.select("a")?.attr("href") "div.entry-content p:has(:matches((?i)(?:S\\s*$seasonSlug|Season\\s*$seasonSlug)))"
} else { }
it.text() to it.nextElementSibling()?.select("a")?.find { child -> val iframeList = detailDoc.select(iSelector).mapNotNull {
child.select("span").text().equals("Episode $episode", true) if (season == null) {
}?.attr("href") it.text() to it.nextElementSibling()?.select("a")?.attr("href")
} } else {
}.filter { it.second?.contains(Regex("(https:)|(http:)")) == true } it.text() to it.nextElementSibling()?.select("a")?.find { child ->
child.select("span").text().equals("Episode $episode", true)
}?.attr("href")
}
}.filter { it.first.contains(Regex("(2160p)|(1080p)")) }
iframeList.apmap { (quality, link) -> iframeList.apmap { (quality, link) ->
val driveLink = val driveLink = bypassHrefli(link ?: return@apmap)
when {
link?.contains("oddfirm") == true -> bypassHrefli(link)
link?.contains("driveleech") == true -> bypassDriveleech(link)
else -> bypassTechmny(link ?: return@apmap)
}
val base = getBaseUrl(driveLink ?: return@apmap) val base = getBaseUrl(driveLink ?: return@apmap)
val driveReq = app.get(driveLink) val driveReq = app.get(driveLink)
val driveRes = driveReq.document val driveRes = driveReq.document
val bitLink = driveRes.select("a.btn.btn-outline-success").attr("href") val bitLink = driveRes.select("a.btn.btn-outline-success").attr("href")
val insLink = val insLink = driveRes.select("a.btn.btn-danger:contains(Instant Download)").attr("href")
driveRes.select("a.btn.btn-danger:contains(Instant Download)").attr("href")
val downloadLink = when { val downloadLink = when {
insLink.isNotEmpty() -> extractInstantUHD(insLink) insLink.isNotEmpty() -> extractInstantUHD(insLink)
driveRes.select("button.btn.btn-success").text() driveRes.select("button.btn.btn-success").text()
.contains("Direct Download", true) -> extractDirectUHD(driveLink, driveReq) .contains("Direct Download", true) -> extractDirectUHD(driveLink, driveReq)
bitLink.isNullOrEmpty() -> { bitLink.isNullOrEmpty() -> {
val backupIframe = driveRes.select("a.btn.btn-outline-warning").attr("href") val backupIframe = driveRes.select("a.btn.btn-outline-warning").attr("href")
extractBackupUHD(backupIframe ?: return@apmap) extractBackupUHD(backupIframe ?: return@apmap)
} }
else -> { else -> {
extractMirrorUHD(bitLink, base) extractMirrorUHD(bitLink, base)
} }
@ -1038,10 +1029,7 @@ object SoraExtractor : SoraStream() {
qualities qualities
) )
) )
} }
} }
suspend fun invokeDotmovies( suspend fun invokeDotmovies(
@ -1103,40 +1091,34 @@ object SoraExtractor : SoraStream() {
1 -> "Season 1" 1 -> "Season 1"
else -> "Season 1 $lastSeason" else -> "Season 1 $lastSeason"
} }
val media = val media = res.selectFirst("div.blog-items article:has(h3.entry-title:matches((?i)$title.*$match)) a")
res.selectFirst("div.blog-items article:has(h3.entry-title:matches((?i)$title.*$match)) a")
?.attr("href") ?.attr("href")
res = app.get(media ?: return).document res = app.get(media ?: return).document
val hTag = if (season == null) "h5" else "h3" val hTag = if (season == null) "h5" else "h3"
val aTag = if (season == null) "Download Now" else "V-Cloud" val aTag = if (season == null) "Download Now" else "V-Cloud"
val sTag = if (season == null) "" else "(Season $season|S$seasonSlug)" val sTag = if (season == null) "" else "(Season $season|S$seasonSlug)"
res.select("div.entry-content > $hTag:matches((?i)$sTag.*(1080p|2160p))") val entry = res.select("div.entry-content > $hTag:matches((?i)$sTag.*(1080p|2160p))")
.filter { element -> !element.text().contains("Download", true) }.apmap { .findLast { element -> !element.text().contains("Download", true) } ?: return
val tags = val tags =
"""(?:1080p|2160p)(.*)""".toRegex().find(it.text())?.groupValues?.get(1)?.trim() """(?:1080p|2160p)(.*)""".toRegex().find(entry.text())?.groupValues?.get(1)?.trim()
val href = val href =
it.nextElementSibling()?.select("a:contains($aTag)")?.attr("href")?.let { url -> entry.nextElementSibling()?.select("a:contains($aTag)")?.attr("href")
app.post( val selector =
"${getBaseUrl(url)}/red.php", if (season == null) "p a:contains(V-Cloud)" else "h4:matches(0?$episode) + p a:contains(V-Cloud)"
data = mapOf("link" to url), val server = app.get(
referer = "$api/" href ?: return, interceptor = CloudflareKiller()
).text.substringAfter("location.href = \"").substringBefore("\"") ).document.selectFirst("div.entry-content > $selector")
} ?.attr("href") ?: return
val selector =
if (season == null) "p a:contains(V-Cloud)" else "h4:matches(0?$episode) + p a:contains(V-Cloud)" loadCustomTagExtractor(
val server = tags,
app.get(href ?: return@apmap).document.selectFirst("div.entry-content > $selector") server,
?.attr("href") "$api/",
loadCustomTagExtractor( subtitleCallback,
tags, callback,
server ?: return@apmap, getIndexQuality(entry.text())
"$api/", )
subtitleCallback,
callback,
getIndexQuality(it.text())
)
}
} }
suspend fun invokeHdmovies4u( suspend fun invokeHdmovies4u(
@ -1213,7 +1195,14 @@ object SoraExtractor : SoraStream() {
iframe.apmap { (iframeLink, title) -> iframe.apmap { (iframeLink, title) ->
val size = Regex("(?i)\\s(\\S+gb|mb)").find(title)?.groupValues?.getOrNull(1) val size = Regex("(?i)\\s(\\S+gb|mb)").find(title)?.groupValues?.getOrNull(1)
loadCustomTagExtractor("[$size]",iframeLink, "$gMoviesAPI/", subtitleCallback, callback, getIndexQuality(title)) loadCustomTagExtractor(
"[$size]",
iframeLink,
"$gMoviesAPI/",
subtitleCallback,
callback,
getIndexQuality(title)
)
} }
} }
@ -1304,7 +1293,9 @@ object SoraExtractor : SoraStream() {
scriptData.firstOrNull() scriptData.firstOrNull()
} else { } else {
scriptData.find { scriptData.find {
it.first.contains(Regex("(?i)$title \\($year\\s?\\)")) && if(season!=null) it.third?.contains("-tvshow-") == true else it.third?.contains("-movie-") == true it.first.contains(Regex("(?i)$title \\($year\\s?\\)")) && if (season != null) it.third?.contains(
"-tvshow-"
) == true else it.third?.contains("-movie-") == true
} }
} }
@ -1527,10 +1518,16 @@ object SoraExtractor : SoraStream() {
iframe?.apmap { iframe?.apmap {
val iframeDoc = app.get(it?.first ?: return@apmap).document val iframeDoc = app.get(it?.first ?: return@apmap).document
val formUrl = iframeDoc.select("form").attr("action") val formUrl = iframeDoc.select("form").attr("action")
val formData = iframeDoc.select("form button").associate { v -> v.attr("name") to v.attr("value") } val formData =
iframeDoc.select("form button").associate { v -> v.attr("name") to v.attr("value") }
val videoUrl = app.post(formUrl, data = formData, referer = it.first).document.selectFirst("div.d-flex.justify-content-center.flex-wrap a")?.attr("href") val videoUrl = app.post(
val quality = Regex("(\\d{3,4})p").find(it.second)?.groupValues?.getOrNull(1)?.toIntOrNull() formUrl,
data = formData,
referer = it.first
).document.selectFirst("div.d-flex.justify-content-center.flex-wrap a")?.attr("href")
val quality =
Regex("(\\d{3,4})p").find(it.second)?.groupValues?.getOrNull(1)?.toIntOrNull()
val qualityName = it.second.replace("${quality}p", "").trim() val qualityName = it.second.replace("${quality}p", "").trim()
callback.invoke( callback.invoke(
@ -1625,27 +1622,32 @@ object SoraExtractor : SoraStream() {
} }
suspend fun invokeSmashyStream( suspend fun invokeSmashyStream(
tmdbId: Int? = null, imdbId: String? = null,
season: Int? = null, season: Int? = null,
episode: Int? = null, episode: Int? = null,
subtitleCallback: (SubtitleFile) -> Unit, subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit, callback: (ExtractorLink) -> Unit,
) { ) {
val url = if (season == null) { val url = if (season == null) {
"$smashyStreamAPI/playere.php?tmdb=$tmdbId" "$smashyStreamAPI/playere.php?imdb=$imdbId"
} else { } else {
"$smashyStreamAPI/playere.php?tmdb=$tmdbId&season=$season&episode=$episode" "$smashyStreamAPI/playere.php?imdb=$imdbId&season=$season&episode=$episode"
} }
app.get( app.get(
url, referer = "https://smashystream.xyz/" url, referer = "https://smashystream.com/"
).document.select("div#_default-servers a.server").map { ).document.select("div#_default-servers a.server").map {
it.attr("data-url") to it.text() it.attr("data-url") to it.text()
}.apmap { }.apmap {
when { when (it.second) {
it.second.contains(Regex("(Player F|Player FM)\$")) -> { "Player F" -> {
invokeSmashyFfix(it.second, it.first, url, callback) invokeSmashyFfix(it.second, it.first, url, callback)
} }
"Player D (Hindi)" -> {
invokeSmashyD(it.first, url, callback)
}
else -> return@apmap else -> return@apmap
} }
} }
@ -1920,14 +1922,15 @@ object SoraExtractor : SoraStream() {
"$twoEmbedAPI/embedtv/$imdbId&s=$season&e=$episode" "$twoEmbedAPI/embedtv/$imdbId&s=$season&e=$episode"
} }
val framesrc = app.get(url).document.selectFirst("iframe#iframesrc")?.attr("data-src") ?: return val framesrc =
app.get(url).document.selectFirst("iframe#iframesrc")?.attr("data-src") ?: return
val ref = getBaseUrl(framesrc) val ref = getBaseUrl(framesrc)
val id = framesrc.substringAfter("id=").substringBefore("&") val id = framesrc.substringAfter("id=").substringBefore("&")
loadExtractor("https://wishfast.top/e/$id", "$ref/", subtitleCallback, callback) loadExtractor("https://wishfast.top/e/$id", "$ref/", subtitleCallback, callback)
} }
suspend fun invokeGomovies( suspend fun invokeOmovies(
title: String? = null, title: String? = null,
year: Int? = null, year: Int? = null,
season: Int? = null, season: Int? = null,
@ -1940,10 +1943,10 @@ object SoraExtractor : SoraStream() {
season, season,
episode, episode,
callback, callback,
"https://gomovies-online.cam", BuildConfig.OMOVIES_API,
"Gomovies", "Omovies",
"_smQamBQsETb", base64Decode("X3NtUWFtQlFzRVRi"),
"_sBWcqbTBMaT" base64Decode("X3NCV2NxYlRCTWFU")
) )
} }
@ -1968,14 +1971,11 @@ object SoraExtractor : SoraStream() {
} else { } else {
"$title Season $season" "$title Season $season"
} }
val idCookies = val savedCookies = mapOf(
mapOf( "_identitygomovies7" to "52fdc70b008c0b1d881dac0f01cca819edd512de01cc8bbc1224ed4aafb78b52a%3A2%3A%7Bi%3A0%3Bs%3A18%3A%22_identitygomovies7%22%3Bi%3A1%3Bs%3A52%3A%22%5B2050366%2C%22HnVRRAObTASOJEr45YyCM8wiHol0V1ko%22%2C2592000%5D%22%3B%7D",
"advanced-frontendgomovies7" to "bjd4n0nnv4hlt4fj5cdjgbrne2",
"_identitygomovies7" to "52fdc70b008c0b1d881dac0f01cca819edd512de01cc8bbc1224ed4aafb78b52a:2:{i:0;s:18:\"_identitygomovies7\";i:1;s:52:\"[2050366,\"HnVRRAObTASOJEr45YyCM8wiHol0V1ko\",2592000]\";}"
) )
val req = app.get("$api/search/$query") val req = app.get("$api/search/$query")
val doc = req.document val doc = req.document
var cookies = req.cookies + idCookies
val media = doc.select("div.$mediaSelector").map { val media = doc.select("div.$mediaSelector").map {
Triple( Triple(
it.attr("data-filmName"), it.attr("data-year"), it.select("a").attr("href") it.attr("data-filmName"), it.attr("data-year"), it.select("a").attr("href")
@ -2003,9 +2003,8 @@ object SoraExtractor : SoraStream() {
fixUrl( fixUrl(
media.third, media.third,
api api
), cookies = cookies )
) )
cookies = cookies + res.cookies
res.document.selectFirst("div#$episodeSelector a:contains(Episode ${slug.second})") res.document.selectFirst("div#$episodeSelector a:contains(Episode ${slug.second})")
?.attr("href") ?.attr("href")
} ?: return } ?: return
@ -2016,11 +2015,11 @@ object SoraExtractor : SoraStream() {
media.third.substringAfterLast("/") to iframe.substringAfterLast("/") media.third.substringAfterLast("/") to iframe.substringAfterLast("/")
.substringBefore("-") .substringBefore("-")
} }
val res = app.get(fixUrl(iframe ?: return, api), cookies = cookies, verify = false) val res = app.get(fixUrl(iframe, api), verify = false)
val serverUrl = res.document.selectFirst("script:containsData(pushState)")?.data()?.let { val serverUrl = res.document.selectFirst("script:containsData(pushState)")?.data()?.let {
""",\s*'([^']+)""".toRegex().find(it)?.groupValues?.get(1) """,\s*'([^']+)""".toRegex().find(it)?.groupValues?.get(1)
} ?: return } ?: return
cookies = cookies + res.cookies val cookies = savedCookies + res.cookies
val url = res.document.select("meta[property=og:url]").attr("content") val url = res.document.select("meta[property=og:url]").attr("content")
val headers = mapOf("X-Requested-With" to "XMLHttpRequest") val headers = mapOf("X-Requested-With" to "XMLHttpRequest")
val qualities = intArrayOf(2160, 1440, 1080, 720, 480, 360) val qualities = intArrayOf(2160, 1440, 1080, 720, 480, 360)
@ -2064,7 +2063,6 @@ object SoraExtractor : SoraStream() {
subtitleCallback: (SubtitleFile) -> Unit, subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit, callback: (ExtractorLink) -> Unit,
) { ) {
val ref = "https://blackvid.space/"
val key = "b6055c533c19131a638c3d2299d525d5ec08a814" val key = "b6055c533c19131a638c3d2299d525d5ec08a814"
val url = if (season == null) { val url = if (season == null) {
"$blackvidAPI/v3/movie/sources/$tmdbId?key=$key" "$blackvidAPI/v3/movie/sources/$tmdbId?key=$key"
@ -2072,7 +2070,8 @@ object SoraExtractor : SoraStream() {
"$blackvidAPI/v3/tv/sources/$tmdbId/$season/$episode?key=$key" "$blackvidAPI/v3/tv/sources/$tmdbId/$season/$episode?key=$key"
} }
val data = app.get(url, timeout = 120L, referer = ref).okhttpResponse.peekBody(1024 * 1024).bytes().decrypt("2378f8e4e844f2dc839ab48f66e00acc2305a401") val data = request(url,).peekBody(1024 * 512).source().buffer.readByteArray()
.decrypt("2378f8e4e844f2dc839ab48f66e00acc2305a401")
val json = tryParseJson<BlackvidResponses>(data) val json = tryParseJson<BlackvidResponses>(data)
json?.sources?.map { source -> json?.sources?.map { source ->
@ -2082,7 +2081,7 @@ object SoraExtractor : SoraStream() {
"Blackvid", "Blackvid",
"Blackvid${source.label}", "Blackvid${source.label}",
s.url ?: return@s, s.url ?: return@s,
ref, "https://blackvid.space/",
if (s.quality.equals("4k")) Qualities.P2160.value else s.quality?.toIntOrNull() if (s.quality.equals("4k")) Qualities.P2160.value else s.quality?.toIntOrNull()
?: Qualities.P1080.value, ?: Qualities.P1080.value,
INFER_TYPE INFER_TYPE
@ -2163,7 +2162,7 @@ object SoraExtractor : SoraStream() {
} }
suspend fun invokeWatchOnline( suspend fun invokeCinemaTv(
imdbId: String? = null, imdbId: String? = null,
title: String? = null, title: String? = null,
year: Int? = null, year: Int? = null,
@ -2175,17 +2174,19 @@ object SoraExtractor : SoraStream() {
val id = imdbId?.removePrefix("tt") val id = imdbId?.removePrefix("tt")
val slug = title.createSlug() val slug = title.createSlug()
val url = if (season == null) { val url = if (season == null) {
"$watchOnlineAPI/movies/play/$id-$slug-$year" "$cinemaTvAPI/movies/play/$id-$slug-$year"
} else { } else {
"$watchOnlineAPI/shows/play/$id-$slug-$year" "$cinemaTvAPI/shows/play/$id-$slug-$year"
} }
val doc = app.get( val specialCookies = "PHPSESSID=e555h63ilisoj2l6j7b5d4jb6p; _csrf=9597150e45f485ad9c4f2e06a2572534d8415337eda9d48d0ecfa25b73b6a9e1a%3A2%3A%7Bi%3A0%3Bs%3A5%3A%22_csrf%22%3Bi%3A1%3Bs%3A32%3A%222HcnegjGB0nX205FAUPb86fqMx9HWIF1%22%3B%7D; _ga=GA1.1.1195498587.1701871187; _ga_VZD7HJ3WK6=GS1.1.$unixTime.4.0.1.$unixTime.0.0.0"
url.replace( val headers = mapOf(
watchOnlineAPI, "Cookie" to specialCookies,
"https://ditairridgeleg.monster" "Connection" to "keep-alive",
) + "?mid=1&sid=9k9iupt5sebbnfajrc6ti3ht7l&sec=1974bc4a902c4d69fcbab261dcec69094a9b8164&t=1694986826984" "x-requested-with" to "XMLHttpRequest",
).document )
val doc = app.get(url, headers = headers).document
val script = doc.selectFirst("script:containsData(hash:)")?.data() val script = doc.selectFirst("script:containsData(hash:)")?.data()
val hash = Regex("hash:\\s*['\"](\\S+)['\"]").find(script ?: return)?.groupValues?.get(1) val hash = Regex("hash:\\s*['\"](\\S+)['\"]").find(script ?: return)?.groupValues?.get(1)
val expires = Regex("expires:\\s*(\\d+)").find(script)?.groupValues?.get(1) val expires = Regex("expires:\\s*(\\d+)").find(script)?.groupValues?.get(1)
@ -2196,24 +2197,24 @@ object SoraExtractor : SoraStream() {
}).let { it.toRegex().find(script)?.groupValues?.get(1) } }).let { it.toRegex().find(script)?.groupValues?.get(1) }
val videoUrl = if (season == null) { val videoUrl = if (season == null) {
"$watchOnlineAPI/api/v1/security/movie-access?id_movie=$episodeId&hash=$hash&expires=$expires" "$cinemaTvAPI/api/v1/security/movie-access?id_movie=$episodeId&hash=$hash&expires=$expires"
} else { } else {
"$watchOnlineAPI/api/v1/security/episode-access?id_episode=$episodeId&hash=$hash&expires=$expires" "$cinemaTvAPI/api/v1/security/episode-access?id_episode=$episodeId&hash=$hash&expires=$expires"
} }
val sources = app.get( val sources = app.get(
videoUrl, videoUrl,
referer = url, referer = url,
headers = mapOf("X-Requested-With" to "XMLHttpRequest") headers = mapOf("X-Requested-With" to "XMLHttpRequest")
).parsedSafe<WatchOnlineResponse>() ).parsedSafe<CinemaTvResponse>()
sources?.streams?.mapKeys { source -> sources?.streams?.mapKeys { source ->
callback.invoke( callback.invoke(
ExtractorLink( ExtractorLink(
"WatchOnline", "CinemaTv",
"WatchOnline", "CinemaTv",
source.value, source.value,
"$watchOnlineAPI/", "$cinemaTvAPI/",
getQualityFromName(source.key), getQualityFromName(source.key),
true true
) )
@ -2225,7 +2226,7 @@ object SoraExtractor : SoraStream() {
subtitleCallback.invoke( subtitleCallback.invoke(
SubtitleFile( SubtitleFile(
sub.language ?: return@map, sub.language ?: return@map,
if (file.startsWith("[")) return@map else fixUrl(file, watchOnlineAPI), if (file.startsWith("[")) return@map else fixUrl(file, cinemaTvAPI),
) )
) )
} }
@ -2248,7 +2249,7 @@ object SoraExtractor : SoraStream() {
val iframe = app.get(url, referer = "https://pressplay.top/").document.selectFirst("iframe") val iframe = app.get(url, referer = "https://pressplay.top/").document.selectFirst("iframe")
?.attr("src") ?.attr("src")
NineTv.getUrl(iframe ?: return, "$nineTvAPI/", subtitleCallback, callback) loadExtractor(iframe ?: return, "$nineTvAPI/", subtitleCallback, callback)
} }
@ -2261,9 +2262,11 @@ object SoraExtractor : SoraStream() {
) { ) {
val referer = "https://bflix.gs/" val referer = "https://bflix.gs/"
val slug = getEpisodeSlug(season, episode) val slug = getEpisodeSlug(season, episode)
var url = if (season == null) "$nowTvAPI/$tmdbId.mp4" else "$nowTvAPI/tv/$tmdbId/s${season}e${slug.second}.mp4" var url =
if (season == null) "$nowTvAPI/$tmdbId.mp4" else "$nowTvAPI/tv/$tmdbId/s${season}e${slug.second}.mp4"
if (!app.get(url, referer = referer).isSuccessful) { if (!app.get(url, referer = referer).isSuccessful) {
url = if (season == null) "$nowTvAPI/$imdbId.mp4" else "$nowTvAPI/tv/$imdbId/s${season}e${slug.second}.mp4" url =
if (season == null) "$nowTvAPI/$imdbId.mp4" else "$nowTvAPI/tv/$imdbId/s${season}e${slug.second}.mp4"
if (!app.get(url, referer = referer).isSuccessful) return if (!app.get(url, referer = referer).isSuccessful) return
} }
callback.invoke( callback.invoke(
@ -2463,7 +2466,7 @@ object SoraExtractor : SoraStream() {
ExtractorLink( ExtractorLink(
"SFMovies", "SFMovies",
"SFMovies", "SFMovies",
fixUrl(video, base64DecodeAPI("aQ==YXA=dGE=ZGE=c3Q=cmU=dC8=bmU=cy4=b3c=bmQ=d2k=ZS4=b3I=LmM=b2I=Ymw=aS4=YXA=dGE=ZGE=c3Q=cmU=Ly8=czo=dHA=aHQ=")), fixUrl(video, getSfServer()),
"", "",
Qualities.P1080.value, Qualities.P1080.value,
INFER_TYPE INFER_TYPE

View File

@ -152,14 +152,14 @@ data class JikanResponse(
@JsonProperty("data") val data: JikanData? = null, @JsonProperty("data") val data: JikanData? = null,
) )
data class WatchOnlineSubtitles( data class CinemaTvSubtitles(
@JsonProperty("language") val language: String? = null, @JsonProperty("language") val language: String? = null,
@JsonProperty("file") val file: Any? = null, @JsonProperty("file") val file: Any? = null,
) )
data class WatchOnlineResponse( data class CinemaTvResponse(
@JsonProperty("streams") val streams: HashMap<String, String>? = null, @JsonProperty("streams") val streams: HashMap<String, String>? = null,
@JsonProperty("subtitles") val subtitles: ArrayList<WatchOnlineSubtitles>? = arrayListOf(), @JsonProperty("subtitles") val subtitles: ArrayList<CinemaTvSubtitles>? = arrayListOf(),
) )
data class VidsrctoResult( data class VidsrctoResult(
@ -424,4 +424,13 @@ data class RidoSearch(
data class SmashySources( data class SmashySources(
@JsonProperty("sourceUrls") var sourceUrls: ArrayList<String>? = arrayListOf(), @JsonProperty("sourceUrls") var sourceUrls: ArrayList<String>? = arrayListOf(),
@JsonProperty("subtitleUrls") var subtitleUrls: String? = null, @JsonProperty("subtitleUrls") var subtitleUrls: String? = null,
)
data class SmashyDSources(
@JsonProperty("sourceUrls") var sourceUrls: ArrayList<SmashyDSourcesUrls>? = arrayListOf(),
)
data class SmashyDSourcesUrls(
@JsonProperty("file") var file: String? = null,
@JsonProperty("title") var title: String? = null,
) )

View File

@ -43,7 +43,8 @@ import com.hexated.SoraExtractor.invokeTvMovies
import com.hexated.SoraExtractor.invokeUhdmovies import com.hexated.SoraExtractor.invokeUhdmovies
import com.hexated.SoraExtractor.invokeVegamovies import com.hexated.SoraExtractor.invokeVegamovies
import com.hexated.SoraExtractor.invokeVidsrcto import com.hexated.SoraExtractor.invokeVidsrcto
import com.hexated.SoraExtractor.invokeWatchOnline import com.hexated.SoraExtractor.invokeCinemaTv
import com.hexated.SoraExtractor.invokeOmovies
import com.hexated.SoraExtractor.invokeWatchsomuch import com.hexated.SoraExtractor.invokeWatchsomuch
import com.hexated.SoraExtractor.invokeZshow import com.hexated.SoraExtractor.invokeZshow
import com.lagradost.cloudstream3.LoadResponse.Companion.addImdbId import com.lagradost.cloudstream3.LoadResponse.Companion.addImdbId
@ -93,10 +94,11 @@ open class SoraStream : TmdbProvider() {
const val flixonAPI = "https://flixon.lol" const val flixonAPI = "https://flixon.lol"
const val smashyStreamAPI = "https://embed.smashystream.com" const val smashyStreamAPI = "https://embed.smashystream.com"
const val watchSomuchAPI = "https://watchsomuch.tv" // sub only const val watchSomuchAPI = "https://watchsomuch.tv" // sub only
const val watchOnlineAPI = "https://lookmovie.foundation" var cinemaTvAPI = BuildConfig.CINEMATV_API
const val nineTvAPI = "https://moviesapi.club" const val nineTvAPI = "https://moviesapi.club"
const val nowTvAPI = "https://myfilestorage.xyz" const val nowTvAPI = "https://myfilestorage.xyz"
const val gokuAPI = "https://goku.sx" const val gokuAPI = "https://goku.sx"
const val zshowAPI = BuildConfig.ZSHOW_API
const val ridomoviesAPI = "https://ridomovies.pw" const val ridomoviesAPI = "https://ridomovies.pw"
const val navyAPI = "https://navy-issue-i-239.site" const val navyAPI = "https://navy-issue-i-239.site"
const val emoviesAPI = "https://emovies.si" const val emoviesAPI = "https://emovies.si"
@ -115,8 +117,8 @@ open class SoraStream : TmdbProvider() {
const val uhdmoviesAPI = "https://uhdmovies.zip" const val uhdmoviesAPI = "https://uhdmovies.zip"
const val gMoviesAPI = "https://gdrivemovies.xyz" const val gMoviesAPI = "https://gdrivemovies.xyz"
const val hdmovies4uAPI = "https://hdmovies4u.band" const val hdmovies4uAPI = "https://hdmovies4u.band"
const val vegaMoviesAPI = "https://vegamovies.boo" const val vegaMoviesAPI = "https://vegamovies.dad"
const val dotmoviesAPI = "https://dotmovies.im" const val dotmoviesAPI = "https://dotmovies.bet"
const val tvMoviesAPI = "https://www.tvseriesnmovies.com" const val tvMoviesAPI = "https://www.tvseriesnmovies.com"
const val moviezAddAPI = "https://ww3.moviezaddiction.click" const val moviezAddAPI = "https://ww3.moviezaddiction.click"
const val bollyMazaAPI = "https://ww3.bollymaza.click" const val bollyMazaAPI = "https://ww3.bollymaza.click"
@ -300,6 +302,7 @@ open class SoraStream : TmdbProvider() {
this.showStatus = getStatus(res.status) this.showStatus = getStatus(res.status)
this.recommendations = recommendations this.recommendations = recommendations
this.actors = actors this.actors = actors
this.contentRating = fetchContentRating(data.id, "US")
addTrailer(trailer) addTrailer(trailer)
addTMDbId(data.id.toString()) addTMDbId(data.id.toString())
addImdbId(res.external_ids?.imdb_id) addImdbId(res.external_ids?.imdb_id)
@ -334,6 +337,7 @@ open class SoraStream : TmdbProvider() {
this.rating = rating this.rating = rating
this.recommendations = recommendations this.recommendations = recommendations
this.actors = actors this.actors = actors
this.contentRating = fetchContentRating(data.id, "US")
addTrailer(trailer) addTrailer(trailer)
addTMDbId(data.id.toString()) addTMDbId(data.id.toString())
addImdbId(res.external_ids?.imdb_id) addImdbId(res.external_ids?.imdb_id)
@ -351,6 +355,15 @@ open class SoraStream : TmdbProvider() {
val res = parseJson<LinkData>(data) val res = parseJson<LinkData>(data)
argamap( argamap(
{
if (!res.isAnime) invokeBlackvid(
res.id,
res.season,
res.episode,
subtitleCallback,
callback
)
},
{ {
invokeDumpStream( invokeDumpStream(
res.title, res.title,
@ -430,7 +443,7 @@ open class SoraStream : TmdbProvider() {
) )
}, },
{ {
invokeKisskh( if (res.isAsian || res.isAnime) invokeKisskh(
res.title, res.title,
res.season, res.season,
res.episode, res.episode,
@ -440,6 +453,15 @@ open class SoraStream : TmdbProvider() {
callback callback
) )
}, },
{
if (!res.isAnime) invokeOmovies (
res.title,
res.year,
res.season,
res.episode,
callback
)
},
{ {
if (!res.isAnime) invokeLing( if (!res.isAnime) invokeLing(
res.title, res.title,
@ -455,7 +477,6 @@ open class SoraStream : TmdbProvider() {
res.title, res.title,
res.year, res.year,
res.season, res.season,
res.lastSeason,
res.episode, res.episode,
callback callback
) )
@ -527,7 +548,7 @@ open class SoraStream : TmdbProvider() {
}, },
{ {
if (!res.isAnime) invokeSmashyStream( if (!res.isAnime) invokeSmashyStream(
res.id, res.imdbId,
res.season, res.season,
res.episode, res.episode,
subtitleCallback, subtitleCallback,
@ -561,7 +582,7 @@ open class SoraStream : TmdbProvider() {
) )
}, },
{ {
invokeWatchOnline( invokeCinemaTv(
res.imdbId, res.imdbId,
res.title, res.title,
res.airedYear ?: res.year, res.airedYear ?: res.year,
@ -672,15 +693,6 @@ open class SoraStream : TmdbProvider() {
callback callback
) )
}, },
{
if (!res.isAnime) invokeBlackvid(
res.id,
res.season,
res.episode,
subtitleCallback,
callback
)
},
{ {
if (!res.isAnime) invokeShowflix( if (!res.isAnime) invokeShowflix(
res.title, res.title,

View File

@ -29,7 +29,8 @@ import com.hexated.SoraExtractor.invokeSFMovies
import com.hexated.SoraExtractor.invokeShowflix import com.hexated.SoraExtractor.invokeShowflix
import com.hexated.SoraExtractor.invokeVidSrc import com.hexated.SoraExtractor.invokeVidSrc
import com.hexated.SoraExtractor.invokeVidsrcto import com.hexated.SoraExtractor.invokeVidsrcto
import com.hexated.SoraExtractor.invokeWatchOnline import com.hexated.SoraExtractor.invokeCinemaTv
import com.hexated.SoraExtractor.invokeOmovies
import com.hexated.SoraExtractor.invokeWatchsomuch import com.hexated.SoraExtractor.invokeWatchsomuch
import com.hexated.SoraExtractor.invokeZshow import com.hexated.SoraExtractor.invokeZshow
import com.lagradost.cloudstream3.SubtitleFile import com.lagradost.cloudstream3.SubtitleFile
@ -133,6 +134,15 @@ class SoraStreamLite : SoraStream() {
callback callback
) )
}, },
{
if (!res.isAnime) invokeOmovies(
res.title,
res.year,
res.season,
res.episode,
callback
)
},
{ {
if (!res.isAnime) invokeKimcartoon( if (!res.isAnime) invokeKimcartoon(
res.title, res.title,
@ -144,7 +154,7 @@ class SoraStreamLite : SoraStream() {
}, },
{ {
if (!res.isAnime) invokeSmashyStream( if (!res.isAnime) invokeSmashyStream(
res.id, res.imdbId,
res.season, res.season,
res.episode, res.episode,
subtitleCallback, subtitleCallback,
@ -161,7 +171,7 @@ class SoraStreamLite : SoraStream() {
) )
}, },
{ {
invokeKisskh( if (res.isAsian || res.isAnime) invokeKisskh(
res.title, res.title,
res.season, res.season,
res.episode, res.episode,
@ -198,7 +208,7 @@ class SoraStreamLite : SoraStream() {
if (!res.isAnime) invokeFlixon(res.id, res.imdbId, res.season, res.episode, callback) if (!res.isAnime) invokeFlixon(res.id, res.imdbId, res.season, res.episode, callback)
}, },
{ {
invokeWatchOnline( invokeCinemaTv(
res.imdbId, res.imdbId,
res.title, res.title,
res.airedYear ?: res.year, res.airedYear ?: res.year,

View File

@ -20,7 +20,6 @@ class SoraStreamPlugin: Plugin() {
registerExtractorAPI(Playm4u()) registerExtractorAPI(Playm4u())
registerExtractorAPI(VCloud()) registerExtractorAPI(VCloud())
registerExtractorAPI(Pixeldra()) registerExtractorAPI(Pixeldra())
registerExtractorAPI(Hubcloud())
registerExtractorAPI(M4ufree()) registerExtractorAPI(M4ufree())
registerExtractorAPI(Streamruby()) registerExtractorAPI(Streamruby())
registerExtractorAPI(Streamwish()) registerExtractorAPI(Streamwish())
@ -28,5 +27,7 @@ class SoraStreamPlugin: Plugin() {
registerExtractorAPI(Embedwish()) registerExtractorAPI(Embedwish())
registerExtractorAPI(Wishfast()) registerExtractorAPI(Wishfast())
registerExtractorAPI(Uploadever()) registerExtractorAPI(Uploadever())
registerExtractorAPI(Netembed())
registerExtractorAPI(Vidplay2())
} }
} }

View File

@ -8,7 +8,6 @@ import com.hexated.SoraStream.Companion.filmxyAPI
import com.hexated.SoraStream.Companion.gdbot import com.hexated.SoraStream.Companion.gdbot
import com.hexated.SoraStream.Companion.hdmovies4uAPI import com.hexated.SoraStream.Companion.hdmovies4uAPI
import com.hexated.SoraStream.Companion.malsyncAPI import com.hexated.SoraStream.Companion.malsyncAPI
import com.hexated.SoraStream.Companion.smashyStreamAPI
import com.hexated.SoraStream.Companion.tvMoviesAPI import com.hexated.SoraStream.Companion.tvMoviesAPI
import com.hexated.SoraStream.Companion.watchflxAPI import com.hexated.SoraStream.Companion.watchflxAPI
import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.*
@ -20,12 +19,16 @@ import com.lagradost.cloudstream3.utils.AppUtils.toJson
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
import com.lagradost.nicehttp.NiceResponse import com.lagradost.nicehttp.NiceResponse
import com.lagradost.nicehttp.RequestBodyTypes import com.lagradost.nicehttp.RequestBodyTypes
import com.lagradost.nicehttp.Requests.Companion.await
import com.lagradost.nicehttp.requestCreator import com.lagradost.nicehttp.requestCreator
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import okhttp3.FormBody import okhttp3.FormBody
import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import java.math.BigInteger import java.math.BigInteger
import java.net.* import java.net.*
@ -34,9 +37,8 @@ import java.security.*
import java.security.spec.PKCS8EncodedKeySpec import java.security.spec.PKCS8EncodedKeySpec
import java.security.spec.X509EncodedKeySpec import java.security.spec.X509EncodedKeySpec
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.time.LocalDate
import java.time.format.DateTimeFormatter
import java.util.* import java.util.*
import java.util.concurrent.TimeUnit
import javax.crypto.Cipher import javax.crypto.Cipher
import javax.crypto.spec.GCMParameterSpec import javax.crypto.spec.GCMParameterSpec
import javax.crypto.spec.IvParameterSpec import javax.crypto.spec.IvParameterSpec
@ -45,7 +47,9 @@ import kotlin.collections.ArrayList
import kotlin.math.min import kotlin.math.min
var watchflxCookies: Map<String, String>? = null var watchflxCookies: Map<String, String>? = null
var filmxyCookies: Map<String,String>? = null var filmxyCookies: Map<String, String>? = null
var sfServer: String? = null
val encodedIndex = arrayOf( val encodedIndex = arrayOf(
"GamMovies", "GamMovies",
"JSMovies", "JSMovies",
@ -93,51 +97,6 @@ val mimeType = arrayOf(
"video/x-msvideo" "video/x-msvideo"
) )
fun String.filterIframe(
seasonNum: Int? = null,
lastSeason: Int? = null,
year: Int?,
title: String?
): Boolean {
val slug = title.createSlug()
val dotSlug = slug?.replace("-", ".")
val spaceSlug = slug?.replace("-", " ")
return if (seasonNum != null) {
if (lastSeason == 1) {
this.contains(Regex("(?i)(S0?$seasonNum)|(Season\\s0?$seasonNum)|(\\d{3,4}p)")) && !this.contains(
"Download",
true
)
} else {
this.contains(Regex("(?i)(S0?$seasonNum)|(Season\\s0?$seasonNum)")) && !this.contains(
"Download",
true
)
}
} else {
this.contains(Regex("(?i)($year)|($dotSlug)|($spaceSlug)")) && !this.contains(
"Download",
true
)
}
}
fun String.filterMedia(title: String?, yearNum: Int?, seasonNum: Int?): Boolean {
val fixTitle = title.createSlug()?.replace("-", " ")
return if (seasonNum != null) {
when {
seasonNum > 1 -> this.contains(Regex("(?i)(Season\\s0?1-0?$seasonNum)|(S0?1-S?0?$seasonNum)")) && this.contains(
Regex("(?i)($fixTitle)|($title)")
)
else -> this.contains(Regex("(?i)(Season\\s0?1)|(S0?1)")) && this.contains(
Regex("(?i)($fixTitle)|($title)")
) && this.contains("$yearNum")
}
} else {
this.contains(Regex("(?i)($fixTitle)|($title)")) && this.contains("$yearNum")
}
}
fun Document.getMirrorLink(): String? { fun Document.getMirrorLink(): String? {
return this.select("div.mb-4 a").randomOrNull() return this.select("div.mb-4 a").randomOrNull()
?.attr("href") ?.attr("href")
@ -450,16 +409,28 @@ suspend fun invokeSmashyFfix(
val json = app.get(url, referer = ref, headers = mapOf("X-Requested-With" to "XMLHttpRequest")) val json = app.get(url, referer = ref, headers = mapOf("X-Requested-With" to "XMLHttpRequest"))
.parsedSafe<SmashySources>() .parsedSafe<SmashySources>()
json?.sourceUrls?.map { json?.sourceUrls?.map {
callback.invoke( M3u8Helper.generateM3u8(
ExtractorLink( "Smashy [$name]",
"Smashy [$name]", it,
"Smashy [$name]", ""
it, ).forEach(callback)
if(name == "Player FM") "https://vidplay.site/" else "", }
Qualities.P1080.value,
INFER_TYPE }
)
) suspend fun invokeSmashyD(
url: String,
ref: String,
callback: (ExtractorLink) -> Unit,
) {
val json = app.get(url, referer = ref, headers = mapOf("X-Requested-With" to "XMLHttpRequest"))
.parsedSafe<SmashyDSources>()
json?.sourceUrls?.apmap {
M3u8Helper.generateM3u8(
"Smashy [Player D ${it.title}]",
it.file ?: return@apmap,
""
).forEach(callback)
} }
} }
@ -487,6 +458,7 @@ suspend fun getDumpIdAndType(title: String?, year: Int?, season: Int?): Pair<Str
true true
) && it.releaseTime == "$year" && it.domainType == 0 ) && it.releaseTime == "$year" && it.domainType == 0
} }
1 -> { 1 -> {
it.name?.contains( it.name?.contains(
"$title", "$title",
@ -496,6 +468,7 @@ suspend fun getDumpIdAndType(title: String?, year: Int?, season: Int?): Pair<Str
true true
)) && it.domainType == 1 )) && it.domainType == 1
} }
else -> { else -> {
it.name?.contains(Regex("(?i)$title\\s?($season|${season.toRomanNumeral()}|Season\\s$season)")) == true && it.releaseTime == "$year" && it.domainType == 1 it.name?.contains(Regex("(?i)$title\\s?($season|${season.toRomanNumeral()}|Season\\s$season)")) == true && it.releaseTime == "$year" && it.domainType == 1
} }
@ -531,25 +504,26 @@ suspend fun invokeDrivetot(
) { ) {
val res = app.get(url) val res = app.get(url)
val data = res.document.select("form input").associate { it.attr("name") to it.attr("value") } val data = res.document.select("form input").associate { it.attr("name") to it.attr("value") }
app.post(res.url, data = data, cookies = res.cookies).document.select("div.card-body a").apmap { ele -> app.post(res.url, data = data, cookies = res.cookies).document.select("div.card-body a")
val href = base64Decode(ele.attr("href").substringAfterLast("/")).let { .apmap { ele ->
if(it.contains("hubcloud.lol")) it.replace("hubcloud.lol", "hubcloud.in") else it val href = base64Decode(ele.attr("href").substringAfterLast("/")).let {
} if (it.contains("hubcloud.lol")) it.replace("hubcloud.lol", "hubcloud.in") else it
loadExtractor(href, "$hdmovies4uAPI/", subtitleCallback) { link -> }
callback.invoke( loadExtractor(href, "$hdmovies4uAPI/", subtitleCallback) { link ->
ExtractorLink( callback.invoke(
link.source, ExtractorLink(
"${link.name} $tags [$size]", link.source,
link.url, "${link.name} $tags [$size]",
link.referer, link.url,
link.quality, link.referer,
link.type, link.quality,
link.headers, link.type,
link.extractorData link.headers,
link.extractorData
)
) )
) }
} }
}
} }
suspend fun bypassBqrecipes(url: String): String? { suspend fun bypassBqrecipes(url: String): String? {
@ -614,28 +588,27 @@ suspend fun bypassFdAds(url: String?): String? {
} }
suspend fun bypassHrefli(url: String): String? { suspend fun bypassHrefli(url: String): String? {
val postUrl = url.substringBefore("?id=").substringAfter("/?") fun Document.getFormUrl() : String {
val res = app.post( return this.select("form#landing").attr("action")
postUrl, data = mapOf( }
"_wp_http" to url.substringAfter("?id=") fun Document.getFormData() : Map<String,String> {
) return this.select("form#landing input").associate { it.attr("name") to it.attr("value") }
).document }
val link = res.select("form#landing").attr("action") val host = getBaseUrl(url)
val wpHttp = res.select("input[name=_wp_http2]").attr("value") var res = app.get(url).document
val token = res.select("input[name=token]").attr("value") var formUrl = res.getFormUrl()
var formData = res.getFormData()
val blogRes = app.post( res = app.post(formUrl, data = formData).document
link, data = mapOf( formUrl = res.getFormUrl()
"_wp_http2" to wpHttp, formData = res.getFormData()
"token" to token
)
).text
val skToken = blogRes.substringAfter("?go=").substringBefore("\"") res = app.post(formUrl, data = formData).document
val skToken = res.selectFirst("script:containsData(?go=)")?.data()?.substringAfter("?go=")?.substringBefore("\"") ?: return null
val driveUrl = app.get( val driveUrl = app.get(
"$postUrl?go=$skToken", cookies = mapOf( "$host?go=$skToken", cookies = mapOf(
skToken to wpHttp skToken to "${formData["_wp_http2"]}"
) )
).document.selectFirst("meta[http-equiv=refresh]")?.attr("content")?.substringAfter("url=") ).document.selectFirst("meta[http-equiv=refresh]")?.attr("content")?.substringAfter("url=")
val path = app.get(driveUrl ?: return null).text.substringAfter("replace(\"") val path = app.get(driveUrl ?: return null).text.substringAfter("replace(\"")
@ -644,94 +617,6 @@ suspend fun bypassHrefli(url: String): String? {
return fixUrl(path, getBaseUrl(driveUrl)) return fixUrl(path, getBaseUrl(driveUrl))
} }
suspend fun bypassTechmny(url: String): String? {
val techRes = app.get(url).document
val postUrl = url.substringBefore("?id=").substringAfter("/?")
val (goUrl, goHeader) = if (techRes.selectFirst("form#landing input[name=_wp_http_c]") != null) {
var res = app.post(
postUrl, data = mapOf(
"_wp_http_c" to url.substringAfter("?id=")
)
)
val (longC, catC, _) = getTechmnyCookies(res.text)
var headers = mapOf("Cookie" to "$longC; $catC")
var formLink = res.document.selectFirst("center a")?.attr("href")
res = app.get(formLink ?: return null, headers = headers)
val (longC2, _, postC) = getTechmnyCookies(res.text)
headers = mapOf("Cookie" to "$catC; $longC2; $postC")
formLink = res.document.selectFirst("center a")?.attr("href")
res = app.get(formLink ?: return null, headers = headers)
val goToken = res.text.substringAfter("?go=").substringBefore("\"")
val tokenUrl = "$postUrl?go=$goToken"
val newLongC = "$goToken=" + longC2.substringAfter("=")
headers = mapOf("Cookie" to "$catC; rdst_post=; $newLongC")
Pair(tokenUrl, headers)
} else {
val secondPage = techRes.getNextTechPage().document
val thirdPage = secondPage.getNextTechPage().text
val goToken = thirdPage.substringAfter("?go=").substringBefore("\"")
val tokenUrl = "$postUrl?go=$goToken"
val headers = mapOf(
"Cookie" to "$goToken=${
secondPage.select("form#landing input[name=_wp_http2]").attr("value")
}"
)
Pair(tokenUrl, headers)
}
val driveUrl =
app.get(goUrl, headers = goHeader).document.selectFirst("meta[http-equiv=refresh]")
?.attr("content")?.substringAfter("url=")
val path = app.get(driveUrl ?: return null).text.substringAfter("replace(\"")
.substringBefore("\")")
if (path == "/404") return null
return fixUrl(path, getBaseUrl(driveUrl))
}
private suspend fun Document.getNextTechPage(): NiceResponse {
return app.post(
this.select("form").attr("action"),
data = this.select("form input").mapNotNull {
it.attr("name") to it.attr("value")
}.toMap().toMutableMap()
)
}
suspend fun bypassDriveleech(url: String): String? {
val path = app.get(url).text.substringAfter("replace(\"")
.substringBefore("\")")
if (path == "/404") return null
return fixUrl(path, getBaseUrl(url))
}
private fun getTechmnyCookies(page: String): Triple<String, String, String> {
val cat = "rdst_cat"
val post = "rdst_post"
val longC = page.substringAfter(".setTime")
.substringAfter("document.cookie = \"")
.substringBefore("\"")
.substringBefore(";")
val catC = if (page.contains("$cat=")) {
page.substringAfterLast("$cat=")
.substringBefore(";").let {
"$cat=$it"
}
} else {
""
}
val postC = if (page.contains("$post=")) {
page.substringAfterLast("$post=")
.substringBefore(";").let {
"$post=$it"
}
} else {
""
}
return Triple(longC, catC, postC)
}
suspend fun getTvMoviesServer(url: String, season: Int?, episode: Int?): Pair<String, String?>? { suspend fun getTvMoviesServer(url: String, season: Int?, episode: Int?): Pair<String, String?>? {
val req = app.get(url) val req = app.get(url)
@ -761,10 +646,20 @@ suspend fun getTvMoviesServer(url: String, season: Int?, episode: Int?): Pair<St
}.lastOrNull() }.lastOrNull()
} }
} }
suspend fun getFilmxyCookies(url: String) = filmxyCookies ?: fetchFilmxyCookies(url).also { filmxyCookies = it }
suspend fun getSfServer() = sfServer ?: fetchSfServer().also { sfServer = it }
suspend fun fetchSfServer(): String {
return app.get("https://raw.githubusercontent.com/hexated/cloudstream-resources/main/sfmovies_server").text
}
suspend fun getFilmxyCookies(url: String) =
filmxyCookies ?: fetchFilmxyCookies(url).also { filmxyCookies = it }
suspend fun fetchFilmxyCookies(url: String): Map<String, String> { suspend fun fetchFilmxyCookies(url: String): Map<String, String> {
val defaultCookies = mutableMapOf("G_ENABLED_IDPS" to "google", "true_checker" to "1", "XID" to "1") val defaultCookies =
mutableMapOf("G_ENABLED_IDPS" to "google", "true_checker" to "1", "XID" to "1")
session.get( session.get(
url, url,
headers = mapOf( headers = mapOf(
@ -777,7 +672,10 @@ suspend fun fetchFilmxyCookies(url: String): Map<String, String> {
defaultCookies["PHPSESSID"] = phpsessid defaultCookies["PHPSESSID"] = phpsessid
val userNonce = val userNonce =
app.get("$filmxyAPI/login/?redirect_to=$filmxyAPI/", cookies = defaultCookies).document.select("script") app.get(
"$filmxyAPI/login/?redirect_to=$filmxyAPI/",
cookies = defaultCookies
).document.select("script")
.find { it.data().contains("var userNonce") }?.data()?.let { .find { it.data().contains("var userNonce") }?.data()?.let {
Regex("var\\suserNonce.*?\"(\\S+?)\";").find(it)?.groupValues?.get(1) Regex("var\\suserNonce.*?\"(\\S+?)\";").find(it)?.groupValues?.get(1)
} }
@ -801,7 +699,8 @@ suspend fun fetchFilmxyCookies(url: String): Map<String, String> {
return cookieJar.plus(defaultCookies) return cookieJar.plus(defaultCookies)
} }
suspend fun getWatchflxCookies() = watchflxCookies ?: fetchWatchflxCookies().also { watchflxCookies = it } suspend fun getWatchflxCookies() =
watchflxCookies ?: fetchWatchflxCookies().also { watchflxCookies = it }
suspend fun fetchWatchflxCookies(): Map<String, String> { suspend fun fetchWatchflxCookies(): Map<String, String> {
session.get(watchflxAPI) session.get(watchflxAPI)
@ -813,7 +712,8 @@ suspend fun fetchWatchflxCookies(): Map<String, String> {
"continue_as_temp" to "true" "continue_as_temp" to "true"
), cookies = cookies, headers = mapOf("X-Requested-With" to "XMLHttpRequest") ), cookies = cookies, headers = mapOf("X-Requested-With" to "XMLHttpRequest")
) )
return session.baseClient.cookieJar.loadForRequest(loginUrl.toHttpUrl()).associate { it.name to it.value } return session.baseClient.cookieJar.loadForRequest(loginUrl.toHttpUrl())
.associate { it.name to it.value }
} }
fun Document.findTvMoviesIframe(): String? { fun Document.findTvMoviesIframe(): String? {
@ -1148,7 +1048,11 @@ fun String.decodePrimewireXor(key: String): String {
fun vidsrctoDecrypt(text: String): String { fun vidsrctoDecrypt(text: String): String {
val parse = Base64.decode(text.toByteArray(), Base64.URL_SAFE) val parse = Base64.decode(text.toByteArray(), Base64.URL_SAFE)
val cipher = Cipher.getInstance("RC4") val cipher = Cipher.getInstance("RC4")
cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec("8z5Ag5wgagfsOuhz".toByteArray(), "RC4"), cipher.parameters) cipher.init(
Cipher.DECRYPT_MODE,
SecretKeySpec("8z5Ag5wgagfsOuhz".toByteArray(), "RC4"),
cipher.parameters
)
return decode(cipher.doFinal(parse).toString(Charsets.UTF_8)) return decode(cipher.doFinal(parse).toString(Charsets.UTF_8))
} }
@ -1288,7 +1192,7 @@ fun isUpcoming(dateString: String?): Boolean {
} }
} }
fun getDate() : TmdbDate { fun getDate(): TmdbDate {
val formatter = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()) val formatter = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
val calender = Calendar.getInstance() val calender = Calendar.getInstance()
val today = formatter.format(calender.time) val today = formatter.format(calender.time)
@ -1373,12 +1277,31 @@ private enum class Symbol(val decimalValue: Int) {
companion object { companion object {
fun closestBelow(value: Int) = fun closestBelow(value: Int) =
values() entries.toTypedArray()
.sortedByDescending { it.decimalValue } .sortedByDescending { it.decimalValue }
.firstOrNull { value >= it.decimalValue } .firstOrNull { value >= it.decimalValue }
} }
} }
suspend fun request(
url: String,
allowRedirects: Boolean = true,
timeout: Long = 60L
): Response {
val client = OkHttpClient().newBuilder()
.connectTimeout(timeout, TimeUnit.SECONDS)
.readTimeout(timeout, TimeUnit.SECONDS)
.writeTimeout(timeout, TimeUnit.SECONDS)
.followRedirects(allowRedirects)
.followSslRedirects(allowRedirects)
.build()
val request: Request = Request.Builder()
.url(url)
.build()
return client.newCall(request).await()
}
object DumpUtils { object DumpUtils {
private val deviceId = getDeviceId() private val deviceId = getDeviceId()
@ -1644,4 +1567,4 @@ object AESGCM {
dateFormat.timeZone = TimeZone.getTimeZone("GMT") dateFormat.timeZone = TimeZone.getTimeZone("GMT")
return dateFormat.format(Date()) return dateFormat.format(Date())
} }
} }

View File

@ -1,5 +1,5 @@
// use an integer for version numbers // use an integer for version numbers
version = 11 version = 12
cloudstream { cloudstream {

View File

@ -13,8 +13,9 @@ import com.lagradost.cloudstream3.utils.AppUtils.toJson
import java.net.URI import java.net.URI
import java.util.ArrayList import java.util.ArrayList
import kotlin.math.roundToInt import kotlin.math.roundToInt
import com.lagradost.cloudstream3.metaproviders.TmdbProvider
open class StremioX : MainAPI() { open class StremioX : TmdbProvider() {
override var mainUrl = "https://torrentio.strem.fun" override var mainUrl = "https://torrentio.strem.fun"
override var name = "StremioX" override var name = "StremioX"
override val hasMainPage = true override val hasMainPage = true
@ -181,6 +182,7 @@ open class StremioX : MainAPI() {
this.showStatus = getStatus(res.status) this.showStatus = getStatus(res.status)
this.recommendations = recommendations this.recommendations = recommendations
this.actors = actors this.actors = actors
this.contentRating = fetchContentRating(data.id, "US")
addTrailer(trailer) addTrailer(trailer)
addTMDbId(data.id.toString()) addTMDbId(data.id.toString())
addImdbId(res.external_ids?.imdb_id) addImdbId(res.external_ids?.imdb_id)
@ -202,6 +204,7 @@ open class StremioX : MainAPI() {
this.rating = rating this.rating = rating
this.recommendations = recommendations this.recommendations = recommendations
this.actors = actors this.actors = actors
this.contentRating = fetchContentRating(data.id, "US")
addTrailer(trailer) addTrailer(trailer)
addTMDbId(data.id.toString()) addTMDbId(data.id.toString())
addImdbId(res.external_ids?.imdb_id) addImdbId(res.external_ids?.imdb_id)

View File

@ -1,12 +1,12 @@
// use an integer for version numbers // use an integer for version numbers
version = 24 version = 25
cloudstream { cloudstream {
language = "hi" language = "hi"
// All of these properties are optional, you can safely remove them // All of these properties are optional, you can safely remove them
description = "Include: Watchomovies" description = "Includes: Watchomovies(NS*W), Max resolution is 720p in both extensions."
authors = listOf("Hexated") authors = listOf("Hexated")
/** /**

View File

@ -5,7 +5,7 @@ import com.lagradost.cloudstream3.TvType
import com.lagradost.cloudstream3.mainPageOf import com.lagradost.cloudstream3.mainPageOf
class Watchomovies : YomoviesProvider() { class Watchomovies : YomoviesProvider() {
override var mainUrl = "https://watchomovies.beauty" override var mainUrl = "https://watchomovies.lat"
override var name = "Watchomovies" override var name = "Watchomovies"
override var lang = "en" override var lang = "en"
override val supportedTypes = setOf( override val supportedTypes = setOf(

View File

@ -10,7 +10,7 @@ import org.jsoup.nodes.Element
import java.net.URI import java.net.URI
open class YomoviesProvider : MainAPI() { open class YomoviesProvider : MainAPI() {
override var mainUrl = "https://yomovies.show" override var mainUrl = "https://yomovies.media"
private var directUrl = "" private var directUrl = ""
override var name = "Yomovies" override var name = "Yomovies"
override val hasMainPage = true override val hasMainPage = true

View File

@ -13,7 +13,7 @@ buildscript {
classpath("com.android.tools.build:gradle:7.0.4") classpath("com.android.tools.build:gradle:7.0.4")
// Cloudstream gradle plugin which makes everything work and builds plugins // Cloudstream gradle plugin which makes everything work and builds plugins
classpath("com.github.recloudstream:gradle:-SNAPSHOT") classpath("com.github.recloudstream:gradle:-SNAPSHOT")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.20") classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.21")
} }
} }