Merge branch 'master' into master

This commit is contained in:
IndusAryan 2023-09-10 00:38:41 +05:30 committed by GitHub
commit 013151104a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
55 changed files with 872 additions and 783 deletions

View file

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

View file

@ -150,8 +150,8 @@ class Animasu : MainAPI() {
link.name, link.name,
link.url, link.url,
link.referer, link.referer,
if(!link.isM3u8) getIndexQuality(quality) else link.quality, if(link.type != ExtractorLinkType.M3U8) getIndexQuality(quality) else link.quality,
link.isM3u8, link.type,
link.headers, link.headers,
link.extractorData link.extractorData
) )

View file

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

View file

@ -60,22 +60,22 @@ class AnimeIndoProvider : MainAPI() {
return if (uri.contains("/anime/")) { return if (uri.contains("/anime/")) {
uri uri
} else { } else {
var title = uri.substringAfter("nonton/") var title = uri.substringAfter("$mainUrl/")
title = when { title = when {
(title.contains("-episode")) && !(title.contains("-movie")) -> Regex("(.+)-episode").find( (title.contains("-episode")) && !(title.contains("-movie")) -> title.substringBefore(
title "-episode"
)?.groupValues?.get(1).toString() )
(title.contains("-movie")) -> Regex("(.+)-movie").find(title)?.groupValues?.get(
1 (title.contains("-movie")) -> title.substringBefore("-movie")
).toString()
else -> title else -> title
} }
"$mainUrl/anime/$title" "$mainUrl/anime/$title"
} }
} }
private fun Element.toSearchResult(): AnimeSearchResponse { private fun Element.toSearchResult(): AnimeSearchResponse {
val title = this.selectFirst("div.titlex, h2.entry-title, h4")?.text()?.trim() ?: "" val title = this.selectFirst("div.title, h2.entry-title, h4")?.text()?.trim() ?: ""
val href = getProperAnimeLink(this.selectFirst("a")!!.attr("href")) val href = getProperAnimeLink(this.selectFirst("a")!!.attr("href"))
val posterUrl = fixUrlNull(this.selectFirst("img")?.attr("src")) val posterUrl = fixUrlNull(this.selectFirst("img")?.attr("src"))
val epNum = this.selectFirst("span.episode")?.ownText()?.replace(Regex("\\D"), "")?.trim() val epNum = this.selectFirst("span.episode")?.ownText()?.replace(Regex("\\D"), "")?.trim()

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

@ -3,6 +3,7 @@ package com.hexated
import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.mvvm.safeApiCall import com.lagradost.cloudstream3.mvvm.safeApiCall
import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.ExtractorLinkType
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.NiceResponse import com.lagradost.nicehttp.NiceResponse
@ -144,7 +145,7 @@ class AnimeSailProvider : MainAPI() {
Jsoup.parse(base64Decode(it.attr("data-em"))).select("iframe").attr("src") Jsoup.parse(base64Decode(it.attr("data-em"))).select("iframe").attr("src")
?: throw ErrorLoadingException("No iframe found") ?: throw ErrorLoadingException("No iframe found")
) )
val quality = getIndexQuality(it.text())
when { when {
iframe.startsWith("$mainUrl/utils/player/arch/") || iframe.startsWith( iframe.startsWith("$mainUrl/utils/player/arch/") || iframe.startsWith(
"$mainUrl/utils/player/race/" "$mainUrl/utils/player/race/"
@ -156,15 +157,13 @@ class AnimeSailProvider : MainAPI() {
iframe.contains("/race/") -> "Race" iframe.contains("/race/") -> "Race"
else -> this.name else -> this.name
} }
val quality =
Regex("\\.(\\d{3,4})\\.").find(link)?.groupValues?.get(1)
callback.invoke( callback.invoke(
ExtractorLink( ExtractorLink(
source = source, source = source,
name = source, name = source,
url = link, url = link,
referer = mainUrl, referer = mainUrl,
quality = quality?.toIntOrNull() ?: Qualities.Unknown.value quality = quality
) )
) )
} }
@ -175,16 +174,16 @@ class AnimeSailProvider : MainAPI() {
val link = "https://rasa-cintaku-semakin-berantai.xyz/v/${ val link = "https://rasa-cintaku-semakin-berantai.xyz/v/${
iframe.substringAfter("id=").substringBefore("&token") iframe.substringAfter("id=").substringBefore("&token")
}" }"
loadExtractor(link, mainUrl, subtitleCallback, callback) loadFixedExtractor(link, quality, mainUrl, subtitleCallback, callback)
} }
iframe.startsWith("$mainUrl/utils/player/framezilla/") || iframe.startsWith("https://uservideo.xyz") -> { iframe.startsWith("$mainUrl/utils/player/framezilla/") || iframe.startsWith("https://uservideo.xyz") -> {
request(iframe, ref = data).document.select("iframe").attr("src") request(iframe, ref = data).document.select("iframe").attr("src")
.let { link -> .let { link ->
loadExtractor(fixUrl(link), mainUrl, subtitleCallback, callback) loadFixedExtractor(fixUrl(link), quality, mainUrl, subtitleCallback, callback)
} }
} }
else -> { else -> {
loadExtractor(iframe, mainUrl, subtitleCallback, callback) loadFixedExtractor(iframe, quality, mainUrl, subtitleCallback, callback)
} }
} }
} }
@ -193,4 +192,32 @@ class AnimeSailProvider : MainAPI() {
return true return true
} }
private suspend fun loadFixedExtractor(
url: String,
quality: Int?,
referer: String? = null,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
) {
loadExtractor(url, referer, subtitleCallback) { link ->
callback.invoke(
ExtractorLink(
link.name,
link.name,
link.url,
link.referer,
if(link.type == ExtractorLinkType.M3U8) link.quality else quality ?: Qualities.Unknown.value,
link.type,
link.headers,
link.extractorData
)
)
}
}
private fun getIndexQuality(str: String): Int {
return Regex("(\\d{3,4})[pP]").find(str)?.groupValues?.getOrNull(1)?.toIntOrNull()
?: Qualities.Unknown.value
}
} }

View file

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

View file

@ -158,7 +158,7 @@ open class Aniworld : MainAPI() {
link.url, link.url,
link.referer, link.referer,
link.quality, link.quality,
link.isM3u8, link.type,
link.headers, link.headers,
link.extractorData link.extractorData
) )

View file

@ -1,12 +1,12 @@
// use an integer for version numbers // use an integer for version numbers
version = 8 version = 11
cloudstream { cloudstream {
language = "id" language = "id"
// All of these properties are optional, you can safely remove them // All of these properties are optional, you can safely remove them
description = "Includes: DutaMovie, Ngefilm, Nodrakorid" description = "Includes: DutaMovie, Ngefilm, Nodrakorid, Multiplex"
authors = listOf("Hexated") authors = listOf("Hexated")
/** /**

View file

@ -1,8 +1,11 @@
package com.hexated package com.hexated
import com.lagradost.cloudstream3.extractors.Filesim import com.lagradost.cloudstream3.extractors.*
import com.lagradost.cloudstream3.extractors.Gdriveplayer
import com.lagradost.cloudstream3.extractors.StreamSB class Doods : DoodLaExtractor() {
override var name = "Doods"
override var mainUrl = "https://doods.pro"
}
class Dutamovie21 : StreamSB() { class Dutamovie21 : StreamSB() {
override var name = "Dutamovie21" override var name = "Dutamovie21"

View file

@ -13,11 +13,13 @@ class GomovPlugin: Plugin() {
registerMainAPI(DutaMovie()) registerMainAPI(DutaMovie())
registerMainAPI(Ngefilm()) registerMainAPI(Ngefilm())
registerMainAPI(Nodrakorid()) registerMainAPI(Nodrakorid())
registerMainAPI(Multiplex())
registerExtractorAPI(FilelionsTo()) registerExtractorAPI(FilelionsTo())
registerExtractorAPI(Likessb()) registerExtractorAPI(Likessb())
registerExtractorAPI(DbGdriveplayer()) registerExtractorAPI(DbGdriveplayer())
registerExtractorAPI(Dutamovie21()) registerExtractorAPI(Dutamovie21())
registerExtractorAPI(Embedwish()) registerExtractorAPI(Embedwish())
registerExtractorAPI(Doods())
registerExtractorAPI(Lylxan()) registerExtractorAPI(Lylxan())
} }
} }

View file

@ -0,0 +1,15 @@
package com.hexated
import com.lagradost.cloudstream3.mainPageOf
class Multiplex : DutaMovie() {
override var mainUrl = "http://5.104.81.46"
override var name = "Multiplex"
override val mainPage = mainPageOf(
"country/usa/page/%d/" to "Movie",
"west-series/page/%d/" to "West Series",
"nonton-drama-korea/page/%d/" to "Drama Korea",
)
}

View file

@ -26,16 +26,30 @@ class Nodrakorid : DutaMovie() {
is TvSeriesLoadResponse -> { is TvSeriesLoadResponse -> {
val doc = app.get(url).document val doc = app.get(url).document
this.comingSoon = false this.comingSoon = false
this.episodes = doc.select("div.entry-content p:contains(Episode)").distinctBy { this.episodes = when {
doc.select("div.vid-episodes a, div.gmr-listseries a").isNotEmpty() -> this.episodes
doc.select("div#download").isEmpty() -> {
doc.select("div.entry-content p:contains(Episode)").distinctBy {
it.text() it.text()
}.map { eps -> }.mapNotNull { eps ->
val num = eps.text()
val endSibling = eps.nextElementSiblings().select("p:contains(Episode)").firstOrNull() ?: eps.nextElementSiblings().select("div.content-moviedata").firstOrNull() val endSibling = eps.nextElementSiblings().select("p:contains(Episode)").firstOrNull() ?: eps.nextElementSiblings().select("div.content-moviedata").firstOrNull()
val siblings = eps.nextElementSiblingsUntil(endSibling).map { ele -> val siblings = eps.nextElementSiblingsUntil(endSibling).map { ele ->
ele.ownText().filter { it.isDigit() }.toIntOrNull() to ele.select("a") ele.ownText().filter { it.isDigit() }.toIntOrNull() to ele.select("a")
.map { it.attr("href") to it.text() } .map { it.attr("href") to it.text() }
}.filter { it.first != null } }.filter { it.first != null }
Episode(siblings.toJson(), episode = Regex("Episode\\s?([0-9]+)").find(num)?.groupValues?.getOrNull(1)?.toIntOrNull()) Episode(siblings.toJson(), episode = eps.text().toEpisode())
}
}
else -> {
doc.select("div#download h3.title-download").mapNotNull { eps ->
val siblings = eps.nextElementSibling()?.select("li")?.map { ele ->
ele.text().filter { it.isDigit() }.toIntOrNull() to ele.select("a").map {
it.attr("href") to it.text().split(" ").first()
}
}?.filter { it.first != null }
Episode(siblings?.toJson() ?: return@mapNotNull null, episode = eps.text().toEpisode())
}
}
} }
} }
} }
@ -83,6 +97,10 @@ class Nodrakorid : DutaMovie() {
} }
} }
private fun String.toEpisode() : Int? {
return Regex("(?i)Episode\\s?([0-9]+)").find(this)?.groupValues?.getOrNull(1)?.toIntOrNull()
}
private fun getBaseUrl(url: String): String { private fun getBaseUrl(url: String): String {
return URI(url).let { return URI(url).let {
"${it.scheme}://${it.host}" "${it.scheme}://${it.host}"
@ -103,8 +121,8 @@ class Nodrakorid : DutaMovie() {
link.name, link.name,
link.url, link.url,
link.referer, link.referer,
if(link.isM3u8) link.quality else quality ?: Qualities.Unknown.value, if(link.type == ExtractorLinkType.M3U8) link.quality else quality ?: Qualities.Unknown.value,
link.isM3u8, link.type,
link.headers, link.headers,
link.extractorData link.extractorData
) )

View file

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

View file

@ -213,7 +213,7 @@ class Hdfilmcehennemi : MainAPI() {
link.url, link.url,
link.referer, link.referer,
link.quality, link.quality,
link.isM3u8, link.type,
link.headers, link.headers,
link.extractorData link.extractorData
) )

View file

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

View file

@ -5,21 +5,24 @@ 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.mvvm.safeApiCall import com.lagradost.cloudstream3.mvvm.safeApiCall
import com.lagradost.cloudstream3.network.CloudflareKiller
import com.lagradost.cloudstream3.utils.* import com.lagradost.cloudstream3.utils.*
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
import com.lagradost.nicehttp.Requests import okhttp3.Interceptor
import com.lagradost.nicehttp.Session import okhttp3.Response
import org.jsoup.Jsoup
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
import java.net.URI import java.net.URI
class IdlixProvider : MainAPI() { class IdlixProvider : MainAPI() {
override var mainUrl = "https://tv.idlixprime.com" override var mainUrl = "https://tv.idlixplus.net"
private var directUrl = mainUrl private var directUrl = mainUrl
override var name = "Idlix" override var name = "Idlix"
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 session = Session(Requests().baseClient) private val cloudflareKiller by lazy { CloudflareKiller() }
private val interceptor by lazy { CloudflareInterceptor(cloudflareKiller) }
override val supportedTypes = setOf( override val supportedTypes = setOf(
TvType.Movie, TvType.Movie,
TvType.TvSeries, TvType.TvSeries,
@ -27,6 +30,8 @@ class IdlixProvider : MainAPI() {
TvType.AsianDrama TvType.AsianDrama
) )
private val key = "\\x5a\\x6d\\x5a\\x6c\\x4e\\x7a\\x55\\x79\\x4d\\x54\\x56\\x6a\\x5a\\x47\\x52\\x69\\x5a\\x44\\x55\\x30\\x5a\\x6d\\x59\\x35\\x4f\\x57\\x45\\x33\\x4d\\x44\\x4a\\x69\\x4e\\x32\\x4a\\x6c\\x4f\\x54\\x42\\x6c\\x4e\\x7a\\x49\\x3d"
override val mainPage = mainPageOf( override val mainPage = mainPageOf(
"$mainUrl/" to "Featured", "$mainUrl/" to "Featured",
"$mainUrl/trending/page/?get=movies" to "Trending Movies", "$mainUrl/trending/page/?get=movies" to "Trending Movies",
@ -51,9 +56,9 @@ class IdlixProvider : MainAPI() {
val url = request.data.split("?") val url = request.data.split("?")
val nonPaged = request.name == "Featured" && page <= 1 val nonPaged = request.name == "Featured" && page <= 1
val req = if (nonPaged) { val req = if (nonPaged) {
session.get(request.data) app.get(request.data, interceptor = interceptor)
} else { } else {
session.get("${url.first()}$page/?${url.lastOrNull()}") app.get("${url.first()}$page/?${url.lastOrNull()}", interceptor = interceptor)
} }
mainUrl = getBaseUrl(req.url) mainUrl = getBaseUrl(req.url)
val document = req.document val document = req.document
@ -93,12 +98,13 @@ class IdlixProvider : MainAPI() {
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 = cloudflareKiller.getCookieHeaders(mainUrl).toMap()
} }
} }
override suspend fun search(query: String): List<SearchResponse> { override suspend fun search(query: String): List<SearchResponse> {
val req = session.get("$mainUrl/search/$query") val req = app.get("$mainUrl/search/$query", interceptor = interceptor)
mainUrl = getBaseUrl(req.url) mainUrl = getBaseUrl(req.url)
val document = req.document val document = req.document
return document.select("div.result-item").map { return document.select("div.result-item").map {
@ -108,12 +114,13 @@ class IdlixProvider : 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 = cloudflareKiller.getCookieHeaders(mainUrl).toMap()
} }
} }
} }
override suspend fun load(url: String): LoadResponse { override suspend fun load(url: String): LoadResponse {
val request = session.get(url) val request = app.get(url, interceptor = interceptor, referer = "$directUrl/")
directUrl = getBaseUrl(request.url) directUrl = getBaseUrl(request.url)
val document = request.document val document = request.document
val title = val title =
@ -142,6 +149,7 @@ class IdlixProvider : MainAPI() {
val recPosterUrl = it.selectFirst("img")?.attr("src").toString() val recPosterUrl = it.selectFirst("img")?.attr("src").toString()
newTvSeriesSearchResponse(recName, recHref, TvType.TvSeries) { newTvSeriesSearchResponse(recName, recHref, TvType.TvSeries) {
this.posterUrl = recPosterUrl this.posterUrl = recPosterUrl
posterHeaders = cloudflareKiller.getCookieHeaders(mainUrl).toMap()
} }
} }
@ -171,6 +179,7 @@ class IdlixProvider : MainAPI() {
addActors(actors) addActors(actors)
this.recommendations = recommendations this.recommendations = recommendations
addTrailer(trailer) addTrailer(trailer)
posterHeaders = cloudflareKiller.getCookieHeaders(mainUrl).toMap()
} }
} else { } else {
newMovieLoadResponse(title, url, TvType.Movie, url) { newMovieLoadResponse(title, url, TvType.Movie, url) {
@ -182,6 +191,7 @@ class IdlixProvider : MainAPI() {
addActors(actors) addActors(actors)
this.recommendations = recommendations this.recommendations = recommendations
addTrailer(trailer) addTrailer(trailer)
posterHeaders = cloudflareKiller.getCookieHeaders(mainUrl).toMap()
} }
} }
} }
@ -193,7 +203,7 @@ class IdlixProvider : MainAPI() {
callback: (ExtractorLink) -> Unit callback: (ExtractorLink) -> Unit
): Boolean { ): Boolean {
val document = session.get(data).document val document = app.get(data, interceptor = interceptor, referer = "$directUrl/").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("/movie/")) "movie" else "tv" val type = if (data.contains("/movie/")) "movie" else "tv"
@ -201,22 +211,20 @@ class IdlixProvider : MainAPI() {
it.attr("data-nume") it.attr("data-nume")
}.apmap { nume -> }.apmap { nume ->
safeApiCall { safeApiCall {
var source = session.post( val source = app.post(
url = "$directUrl/wp-admin/admin-ajax.php", url = "$directUrl/wp-admin/admin-ajax.php", data = mapOf(
data = mapOf( "action" to "doo_player_ajax", "post" to id, "nume" to nume, "type" to type
"action" to "doo_player_ajax", ), headers = mapOf("X-Requested-With" to "XMLHttpRequest"), referer = data, interceptor = interceptor
"post" to id, ).let { tryParseJson<ResponseHash>(it.text) } ?: return@safeApiCall
"nume" to nume,
"type" to type
),
headers = mapOf("X-Requested-With" to "XMLHttpRequest"),
referer = data
).let { tryParseJson<ResponseHash>(it.text) }?.embed_url ?: return@safeApiCall
if (source.startsWith("https://uservideo.xyz")) { val password = if(source.key?.startsWith("\\x") == true) source.key else key
source = app.get(source).document.select("iframe").attr("src") var decrypted = AesHelper.cryptoAESHandler(source.embed_url, password.toByteArray(), false)?.fixBloat() ?: return@safeApiCall
if (decrypted.startsWith("https://uservideo.xyz")) {
decrypted = app.get(decrypted).document.select("iframe").attr("src")
} }
loadExtractor(source, directUrl, subtitleCallback, callback)
getUrl(decrypted, "$directUrl/", subtitleCallback, callback)
} }
} }
@ -224,9 +232,86 @@ class IdlixProvider : MainAPI() {
return true return true
} }
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
}
}
private fun String.fixBloat() : String {
return this.replace("\"", "").replace("\\", "")
}
private suspend fun getUrl(
url: String,
referer: String?,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
) {
val document = app.get(url, referer = referer).document
val hash = url.split("/").last().substringAfter("data=")
val m3uLink = app.post(
url = "$mainUrl/player/index.php?data=$hash&do=getVideo",
data = mapOf("hash" to hash, "r" to "$referer"),
referer = referer,
headers = mapOf("X-Requested-With" to "XMLHttpRequest")
).parsed<ResponseSource>().videoSource
M3u8Helper.generateM3u8(
this.name,
m3uLink,
"$referer",
).forEach(callback)
document.select("script").map { script ->
if (script.data().contains("eval(function(p,a,c,k,e,d)")) {
val subData =
getAndUnpack(script.data()).substringAfter("\"tracks\":[").substringBefore("],")
tryParseJson<List<Tracks>>("[$subData]")?.map { subtitle ->
subtitleCallback.invoke(
SubtitleFile(
getLanguage(subtitle.label ?: ""),
subtitle.file
)
)
}
}
}
}
private fun getLanguage(str: String): String {
return when {
str.contains("indonesia", true) || str
.contains("bahasa", true) -> "Indonesian"
else -> str
}
}
data class ResponseSource(
@JsonProperty("hls") val hls: Boolean,
@JsonProperty("videoSource") val videoSource: String,
@JsonProperty("securedLink") val securedLink: String?,
)
data class Tracks(
@JsonProperty("kind") val kind: String?,
@JsonProperty("file") val file: String,
@JsonProperty("label") val label: String?,
)
data class ResponseHash( data class ResponseHash(
@JsonProperty("embed_url") val embed_url: String, @JsonProperty("embed_url") val embed_url: String,
@JsonProperty("key") val key: String?,
@JsonProperty("type") val type: String?, @JsonProperty("type") val type: String?,
) )
} }

View file

@ -0,0 +1,95 @@
package com.hexated
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.ErrorLoadingException
import com.lagradost.cloudstream3.base64DecodeArray
import com.lagradost.cloudstream3.base64Encode
import com.lagradost.cloudstream3.utils.AppUtils
import java.security.DigestException
import java.security.MessageDigest
import javax.crypto.Cipher
import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.SecretKeySpec
object AesHelper {
fun cryptoAESHandler(
data: String,
pass: ByteArray,
encrypt: Boolean = true,
padding: String = "AES/CBC/PKCS5PADDING",
): String? {
val parse = AppUtils.tryParseJson<AesData>(data) ?: return null
val (key, iv) = generateKeyAndIv(pass, parse.s.hexToByteArray()) ?: throw ErrorLoadingException("failed to generate key")
val cipher = Cipher.getInstance(padding)
return if (!encrypt) {
cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(key, "AES"), IvParameterSpec(iv))
String(cipher.doFinal(base64DecodeArray(parse.ct)))
} else {
cipher.init(Cipher.ENCRYPT_MODE, SecretKeySpec(key, "AES"), IvParameterSpec(iv))
base64Encode(cipher.doFinal(parse.ct.toByteArray()))
}
}
// https://stackoverflow.com/a/41434590/8166854
private fun generateKeyAndIv(
password: ByteArray,
salt: ByteArray,
hashAlgorithm: String = "MD5",
keyLength: Int = 32,
ivLength: Int = 16,
iterations: Int = 1
): List<ByteArray>? {
val md = MessageDigest.getInstance(hashAlgorithm)
val digestLength = md.digestLength
val targetKeySize = keyLength + ivLength
val requiredLength = (targetKeySize + digestLength - 1) / digestLength * digestLength
val generatedData = ByteArray(requiredLength)
var generatedLength = 0
try {
md.reset()
while (generatedLength < targetKeySize) {
if (generatedLength > 0)
md.update(
generatedData,
generatedLength - digestLength,
digestLength
)
md.update(password)
md.update(salt, 0, 8)
md.digest(generatedData, generatedLength, digestLength)
for (i in 1 until iterations) {
md.update(generatedData, generatedLength, digestLength)
md.digest(generatedData, generatedLength, digestLength)
}
generatedLength += digestLength
}
return listOf(
generatedData.copyOfRange(0, keyLength),
generatedData.copyOfRange(keyLength, targetKeySize)
)
} catch (e: DigestException) {
return null
}
}
private fun String.hexToByteArray(): ByteArray {
check(length % 2 == 0) { "Must have an even length" }
return chunked(2)
.map { it.toInt(16).toByte() }
.toByteArray()
}
private data class AesData(
@JsonProperty("ct") val ct: String,
@JsonProperty("iv") val iv: String,
@JsonProperty("s") val s: String
)
}

View file

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

View file

@ -3,6 +3,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.LoadResponse.Companion.addTrailer import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
import com.lagradost.cloudstream3.extractors.helper.AesHelper
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.Qualities import com.lagradost.cloudstream3.utils.Qualities
@ -10,12 +11,7 @@ import com.lagradost.cloudstream3.utils.getQualityFromName
import com.lagradost.cloudstream3.utils.loadExtractor import com.lagradost.cloudstream3.utils.loadExtractor
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
import java.net.URI import java.net.URI
import java.security.DigestException
import java.security.MessageDigest
import java.util.ArrayList import java.util.ArrayList
import javax.crypto.Cipher
import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.SecretKeySpec
class KuronimeProvider : MainAPI() { class KuronimeProvider : MainAPI() {
override var mainUrl = "https://45.12.2.26" override var mainUrl = "https://45.12.2.26"
@ -186,10 +182,11 @@ class KuronimeProvider : MainAPI() {
argamap( argamap(
{ {
val decrypt = cryptoAES( val decrypt = AesHelper.cryptoAESHandler(
servers?.src ?: return@argamap, base64Decode(servers?.src ?: return@argamap),
KEY.toByteArray(), KEY.toByteArray(),
false false,
"AES/CBC/NoPadding"
) )
val source = val source =
tryParseJson<Sources>(decrypt?.toJsonFormat())?.src?.replace("\\", "") tryParseJson<Sources>(decrypt?.toJsonFormat())?.src?.replace("\\", "")
@ -206,10 +203,11 @@ class KuronimeProvider : MainAPI() {
) )
}, },
{ {
val decrypt = cryptoAES( val decrypt = AesHelper.cryptoAESHandler(
servers?.mirror ?: return@argamap, base64Decode(servers?.mirror ?: return@argamap),
KEY.toByteArray(), KEY.toByteArray(),
false false,
"AES/CBC/NoPadding"
) )
tryParseJson<Mirrors>(decrypt)?.embed?.map { embed -> tryParseJson<Mirrors>(decrypt)?.embed?.map { embed ->
embed.value.apmap { embed.value.apmap {
@ -249,7 +247,7 @@ class KuronimeProvider : MainAPI() {
link.url, link.url,
link.referer, link.referer,
getQualityFromName(quality), getQualityFromName(quality),
link.isM3u8, link.type,
link.headers, link.headers,
link.extractorData link.extractorData
) )
@ -263,86 +261,6 @@ class KuronimeProvider : MainAPI() {
} }
} }
// https://stackoverflow.com/a/41434590/8166854
private fun generateKeyAndIv(
password: ByteArray,
salt: ByteArray,
hashAlgorithm: String = "MD5",
keyLength: Int = 32,
ivLength: Int = 16,
iterations: Int = 1
): List<ByteArray>? {
val md = MessageDigest.getInstance(hashAlgorithm)
val digestLength = md.digestLength
val targetKeySize = keyLength + ivLength
val requiredLength = (targetKeySize + digestLength - 1) / digestLength * digestLength
val generatedData = ByteArray(requiredLength)
var generatedLength = 0
try {
md.reset()
while (generatedLength < targetKeySize) {
if (generatedLength > 0)
md.update(
generatedData,
generatedLength - digestLength,
digestLength
)
md.update(password)
md.update(salt, 0, 8)
md.digest(generatedData, generatedLength, digestLength)
for (i in 1 until iterations) {
md.update(generatedData, generatedLength, digestLength)
md.digest(generatedData, generatedLength, digestLength)
}
generatedLength += digestLength
}
return listOf(
generatedData.copyOfRange(0, keyLength),
generatedData.copyOfRange(keyLength, targetKeySize)
)
} catch (e: DigestException) {
return null
}
}
private fun String.decodeHex(): ByteArray {
check(length % 2 == 0) { "Must have an even length" }
return chunked(2)
.map { it.toInt(16).toByte() }
.toByteArray()
}
private fun cryptoAES(
data: String,
pass: ByteArray,
encrypt: Boolean = true
): String? {
val json = tryParseJson<AesData>(base64Decode(data))
?: throw ErrorLoadingException("No Data Found")
val (key, iv) = generateKeyAndIv(pass, json.s.decodeHex()) ?: return null
val cipher = Cipher.getInstance("AES/CBC/NoPadding")
return if (!encrypt) {
cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(key, "AES"), IvParameterSpec(iv))
String(cipher.doFinal(base64DecodeArray(json.ct)))
} else {
cipher.init(Cipher.ENCRYPT_MODE, SecretKeySpec(key, "AES"), IvParameterSpec(iv))
base64Encode(cipher.doFinal(json.ct.toByteArray()))
}
}
data class AesData(
@JsonProperty("ct") val ct: String,
@JsonProperty("iv") val iv: String,
@JsonProperty("s") val s: String
)
data class Mirrors( data class Mirrors(
@JsonProperty("embed") val embed: Map<String, Map<String, String>> = emptyMap(), @JsonProperty("embed") val embed: Map<String, Map<String, String>> = emptyMap(),
) )

View file

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

View file

@ -9,7 +9,7 @@ import org.jsoup.nodes.Element
import java.net.URLDecoder import java.net.URLDecoder
class LayarKacaProvider : MainAPI() { class LayarKacaProvider : MainAPI() {
override var mainUrl = "https://tv1.lk21official.pro" override var mainUrl = "https://tv3.lk21official.pro"
private var seriesUrl = "https://tv1.nontondrama.click" private var seriesUrl = "https://tv1.nontondrama.click"
override var name = "LayarKaca" override var name = "LayarKaca"
override val hasMainPage = true override val hasMainPage = true
@ -72,11 +72,11 @@ class LayarKacaProvider : MainAPI() {
} }
override suspend fun search(query: String): List<SearchResponse> { override suspend fun search(query: String): List<SearchResponse> {
val document = app.get("$mainUrl/?s=$query").document val document = app.get("$mainUrl/search.php?s=$query").document
return document.select("div.search-item").map { return document.select("div.search-item").mapNotNull {
val title = it.selectFirst("h2 > a")!!.text().trim() val title = it.selectFirst("a")?.attr("title") ?: ""
val href = fixUrl(it.selectFirst("a")!!.attr("href")) val href = fixUrl(it.selectFirst("a")?.attr("href") ?: return@mapNotNull null)
val posterUrl = fixUrl(it.selectFirst("img.img-thumbnail")?.attr("src").toString()) val posterUrl = fixUrlNull(it.selectFirst("img.img-thumbnail")?.attr("src"))
newTvSeriesSearchResponse(title, href, TvType.TvSeries) { newTvSeriesSearchResponse(title, href, TvType.TvSeries) {
this.posterUrl = posterUrl this.posterUrl = posterUrl
} }
@ -172,8 +172,6 @@ class LayarKacaProvider : MainAPI() {
return app.get(this, referer = "$seriesUrl/").document.select("div.embed iframe").attr("src") return app.get(this, referer = "$seriesUrl/").document.select("div.embed iframe").attr("src")
} }
private fun decode(input: String): String = URLDecoder.decode(input, "utf-8").replace(" ", "%20")
} }
open class Emturbovid : ExtractorApi() { open class Emturbovid : ExtractorApi() {

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

@ -16,6 +16,11 @@ class Paistream : Streampai() {
override val mainUrl = "https://paistream.my.id" override val mainUrl = "https://paistream.my.id"
} }
class TvMinioppai : Streampai() {
override val name = "Minioppai"
override val mainUrl = "https://tv.minioppai.org"
}
open class Streampai : ExtractorApi() { open class Streampai : ExtractorApi() {
override val name = "Streampai" override val name = "Streampai"
override val mainUrl = "https://streampai.my.id" override val mainUrl = "https://streampai.my.id"

View file

@ -47,7 +47,7 @@ class Minioppai : MainAPI() {
override val mainPage = mainPageOf( override val mainPage = mainPageOf(
"$mainUrl/watch" to "New Episode", "$mainUrl/watch" to "New Episode",
"$mainUrl/popular" to "Popular Hentai", "$mainUrl/populars" to "Popular Hentai",
) )
override suspend fun getMainPage( override suspend fun getMainPage(

View file

@ -11,5 +11,6 @@ class MinioppaiPlugin: Plugin() {
registerMainAPI(Minioppai()) registerMainAPI(Minioppai())
registerExtractorAPI(Streampai()) registerExtractorAPI(Streampai())
registerExtractorAPI(Paistream()) registerExtractorAPI(Paistream())
registerExtractorAPI(TvMinioppai())
} }
} }

View file

@ -11,7 +11,9 @@ import com.lagradost.cloudstream3.utils.loadExtractor
import org.jsoup.Jsoup import org.jsoup.Jsoup
class Hdmovie2 : Movierulzhd() { class Hdmovie2 : Movierulzhd() {
override var mainUrl = "https://hdmovie2.codes" override var mainUrl = "https://hdmovie2.codes"
override var name = "Hdmovie2" override var name = "Hdmovie2"
override val mainPage = mainPageOf( override val mainPage = mainPageOf(

View file

@ -11,7 +11,9 @@ import org.jsoup.nodes.Element
import java.net.URI import java.net.URI
open class Movierulzhd : MainAPI() { open class Movierulzhd : MainAPI() {
override var mainUrl = "https://movierulzvid.gold" override var mainUrl = "https://movierulzvid.gold"
var directUrl = "" var directUrl = ""
override var name = "Movierulzhd" override var name = "Movierulzhd"
override val hasMainPage = true override val hasMainPage = true

View file

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

View file

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

View file

@ -1,188 +0,0 @@
package com.hexated
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.LoadResponse.Companion.addActors
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.getQualityFromName
import org.jsoup.nodes.Element
class MultiplexProvider : MainAPI() {
override var mainUrl = "http://5.104.81.46"
override var name = "Multiplex"
override val hasMainPage = true
override var lang = "id"
override val hasDownloadSupport = true
override val supportedTypes = setOf(
TvType.Movie,
TvType.TvSeries,
TvType.AsianDrama
)
override val mainPage = mainPageOf(
"$mainUrl/genre/top-popular-movies/page/" to "Top Popolar Movies",
"$mainUrl/genre/series-ongoing/page/" to "Series Ongoing",
"$mainUrl/genre/series-barat/page/" to "Series Barat",
"$mainUrl/genre/series-korea/page/" to "Series Korea",
)
override suspend fun getMainPage(
page: Int,
request: MainPageRequest
): HomePageResponse {
val document = app.get(request.data + page).document
val home = document.select("article.item").mapNotNull {
it.toSearchResult()
}
return newHomePageResponse(request.name, home)
}
private fun Element.toSearchResult(): SearchResponse? {
val title = this.selectFirst("h2.entry-title > a")?.text()?.trim() ?: return null
val href = fixUrl(this.selectFirst("a")!!.attr("href"))
val posterUrl = fixUrlNull(this.selectFirst("a > img")?.attr("data-src"))
val quality = this.select("div.gmr-quality-item > a").text().trim()
return if (quality.isEmpty()) {
val episode = this.select("div.gmr-numbeps > span").text().toIntOrNull()
newAnimeSearchResponse(title, href, TvType.TvSeries) {
this.posterUrl = posterUrl
addSub(episode)
}
} else {
newMovieSearchResponse(title, href, TvType.Movie) {
this.posterUrl = posterUrl
addQuality(quality)
}
}
}
private fun Element.toBottomSearchResult(): SearchResponse? {
val title = this.selectFirst("a > span.idmuvi-rp-title")?.text()?.trim() ?: return null
val href = this.selectFirst("a")!!.attr("href")
val posterUrl = fixUrl(this.selectFirst("a > img")?.attr("data-src").toString())
return newMovieSearchResponse(title, href, TvType.Movie) {
this.posterUrl = posterUrl
}
}
override suspend fun search(query: String): List<SearchResponse> {
val link = "$mainUrl/?s=$query&post_type[]=post&post_type[]=tv"
val document = app.get(link).document
return document.select("article.item").mapNotNull {
it.toSearchResult()
}
}
override suspend fun load(url: String): LoadResponse {
val document = app.get(url).document
val title =
document.selectFirst("h1.entry-title")?.text()?.substringBefore("Season")?.trim()
.toString()
val poster =
fixUrl(document.selectFirst("figure.pull-left > img")?.attr("data-src").toString())
val tags = document.select("span.gmr-movie-genre:contains(Genre:) > a").map { it.text() }
val year =
document.select("span.gmr-movie-genre:contains(Year:) > a").text().trim().toIntOrNull()
val tvType = if (url.contains("/tv/")) TvType.TvSeries else TvType.Movie
val description = document.selectFirst("div[itemprop=description] > p")?.text()?.trim()
val trailer = document.selectFirst("ul.gmr-player-nav li a.gmr-trailer-popup")?.attr("href")
val rating =
document.selectFirst("div.gmr-meta-rating > span[itemprop=ratingValue]")?.text()
?.toRatingInt()
val actors = document.select("div.gmr-moviedata").last()?.select("span[itemprop=actors]")
?.map { it.select("a").text() }
val recommendations = document.select("div.idmuvi-rp ul li").mapNotNull {
it.toBottomSearchResult()
}
return if (tvType == TvType.TvSeries) {
val episodes = document.select("div.gmr-listseries > a").map {
val href = fixUrl(it.attr("href"))
val episode = it.text().split(" ").last().toIntOrNull()
val season = it.text().split(" ").first().substringAfter("S").toIntOrNull()
Episode(
href,
"Episode $episode",
season,
episode,
)
}
newTvSeriesLoadResponse(title, url, TvType.TvSeries, episodes) {
this.posterUrl = poster
this.year = year
this.plot = description
this.tags = tags
this.rating = rating
addActors(actors)
this.recommendations = recommendations
addTrailer(trailer)
}
} else {
newMovieLoadResponse(title, url, TvType.Movie, url) {
this.posterUrl = poster
this.year = year
this.plot = description
this.tags = tags
this.rating = rating
addActors(actors)
this.recommendations = recommendations
addTrailer(trailer)
}
}
}
private data class ResponseSource(
@JsonProperty("file") val file: String,
@JsonProperty("type") val type: String?,
@JsonProperty("label") val label: String?
)
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
val document = app.get(data).document
val id = document.selectFirst("div#muvipro_player_content_id")!!.attr("data-id")
val server = app.post(
"$mainUrl/wp-admin/admin-ajax.php",
data = mapOf("action" to "muvipro_player_content", "tab" to "player1", "post_id" to id)
).document.select("iframe").attr("src")
app.get(server, referer = "$mainUrl/").document.select("script").map { script ->
if (script.data().contains("var config = {")) {
val source = script.data().substringAfter("sources: [").substringBefore("],")
tryParseJson<List<ResponseSource>>("[$source]")?.map { m3u ->
val m3uData = app.get(m3u.file, referer = "https://gdriveplayer.link/").text
val quality =
Regex("\\d{3,4}\\.m3u8").findAll(m3uData).map { it.value }.toList()
quality.forEach {
callback.invoke(
ExtractorLink(
source = name,
name = name,
url = m3u.file.replace("video.m3u8", it),
referer = "https://gdriveplayer.link/",
quality = getQualityFromName("${it.replace(".m3u8", "")}p"),
isM3u8 = true
)
)
}
}
}
}
return true
}
}

View file

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

View file

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

View file

@ -170,8 +170,8 @@ class Nekopoi : MainAPI() {
link.name, link.name,
link.url, link.url,
link.referer, link.referer,
if (link.isM3u8) link.quality else it.first, if (link.type == ExtractorLinkType.M3U8) link.quality else it.first,
link.isM3u8, link.type,
link.headers, link.headers,
link.extractorData link.extractorData
) )

View file

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

View file

@ -164,7 +164,7 @@ class Nimegami : MainAPI() {
link.url, link.url,
link.referer, link.referer,
getQualityFromName(quality), getQualityFromName(quality),
link.isM3u8, link.type,
link.headers, link.headers,
link.extractorData link.extractorData
) )

View file

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

View file

@ -0,0 +1,45 @@
package com.hexated
import com.lagradost.cloudstream3.SubtitleFile
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.utils.*
open class Qiwi : ExtractorApi() {
override val name = "Qiwi"
override val mainUrl = "https://qiwi.gg"
override val requiresReferer = true
override suspend fun getUrl(
url: String,
referer: String?,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
) {
val document = app.get(url, referer = referer).document
val title = document.select("title").text()
val source = document.select("video source").attr("src")
callback.invoke(
ExtractorLink(
this.name,
this.name,
source,
"$mainUrl/",
getIndexQuality(title),
headers = mapOf(
"Accept" to "video/webm,video/ogg,video/*;q=0.9,application/ogg;q=0.7,audio/*;q=0.6,*/*;q=0.5",
"Range" to "bytes=0-",
"Sec-Fetch-Dest" to "video",
"Sec-Fetch-Mode" to "no-cors",
)
)
)
}
private fun getIndexQuality(str: String): Int {
return Regex("(\\d{3,4})[pP]").find(str)?.groupValues?.getOrNull(1)?.toIntOrNull()
?: Qualities.Unknown.value
}
}

View file

@ -6,7 +6,7 @@ import com.lagradost.cloudstream3.utils.*
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
class OploverzProvider : MainAPI() { class OploverzProvider : MainAPI() {
override var mainUrl = "https://oploverz.team" override var mainUrl = "https://oploverz.red"
override var name = "Oploverz" override var name = "Oploverz"
override val hasMainPage = true override val hasMainPage = true
override var lang = "id" override var lang = "id"
@ -195,7 +195,7 @@ class OploverzProvider : MainAPI() {
link.url, link.url,
link.referer, link.referer,
name.fixQuality(), name.fixQuality(),
link.isM3u8, link.type,
link.headers, link.headers,
link.extractorData link.extractorData
) )

View file

@ -10,5 +10,6 @@ class OploverzProviderPlugin: Plugin() {
override fun load(context: Context) { override fun load(context: Context) {
// 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(OploverzProvider()) registerMainAPI(OploverzProvider())
registerExtractorAPI(Qiwi())
} }
} }

View file

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

View file

@ -236,7 +236,7 @@ class OtakudesuProvider : MainAPI() {
link.url, link.url,
link.referer, link.referer,
quality, quality,
link.isM3u8, link.type,
link.headers, link.headers,
link.extractorData link.extractorData
) )

View file

@ -9,7 +9,7 @@ import org.jsoup.nodes.Element
import java.net.URLDecoder import java.net.URLDecoder
class PhimmoichillProvider : MainAPI() { class PhimmoichillProvider : MainAPI() {
override var mainUrl = "https://phimmoichilld.net" override var mainUrl = "https://phimmoichillg.net"
override var name = "Phimmoichill" override var name = "Phimmoichill"
override val hasMainPage = true override val hasMainPage = true
override var lang = "vi" override var lang = "vi"

View file

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

View file

@ -203,7 +203,7 @@ class Samehadaku : MainAPI() {
link.url, link.url,
link.referer, link.referer,
name.fixQuality(), name.fixQuality(),
link.isM3u8, link.type,
link.headers, link.headers,
link.extractorData link.extractorData
) )

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 = 159 version = 164
android { android {
defaultConfig { defaultConfig {

View file

@ -0,0 +1,162 @@
package com.hexated
import com.lagradost.cloudstream3.extractors.Filesim
import com.lagradost.cloudstream3.extractors.GMPlayer
import com.lagradost.cloudstream3.extractors.StreamSB
import com.lagradost.cloudstream3.extractors.Voe
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.APIHolder.getCaptchaToken
import com.lagradost.cloudstream3.SubtitleFile
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.*
import java.math.BigInteger
import java.security.MessageDigest
open class Playm4u : ExtractorApi() {
override val name = "Playm4u"
override val mainUrl = "https://play9str.playm4u.xyz"
override val requiresReferer = true
private val password = "plhq@@@22"
override suspend fun getUrl(
url: String,
referer: String?,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
) {
val document = app.get(url, referer = referer).document
val script = document.selectFirst("script:containsData(idfile =)")?.data() ?: return
val passScript = document.selectFirst("script:containsData(domain_ref =)")?.data() ?: return
val pass = passScript.substringAfter("CryptoJS.MD5('").substringBefore("')")
val amount = passScript.substringAfter(".toString()), ").substringBefore("));").toInt()
val idFile = "idfile\\s*=\\s*[\"'](\\S+)[\"'];".findIn(script)
val idUser = "idUser\\s*=\\s*[\"'](\\S+)[\"'];".findIn(script)
val domainApi = "DOMAIN_API\\s*=\\s*[\"'](\\S+)[\"'];".findIn(script)
val nameKeyV3 = "NameKeyV3\\s*=\\s*[\"'](\\S+)[\"'];".findIn(script)
val dataEnc = caesarShift(
mahoa(
"Win32|$idUser|$idFile|$referer",
md5(pass)
), amount
).toHex()
val captchaKey =
document.select("script[src*=https://www.google.com/recaptcha/api.js?render=]")
.attr("src").substringAfter("render=")
val token = getCaptchaToken(
url,
captchaKey,
referer = referer
)
val source = app.post(
domainApi, data = mapOf(
"namekey" to nameKeyV3,
"token" to "$token",
"referrer" to "$referer",
"data" to "$dataEnc|${md5(dataEnc + password)}",
), referer = "$mainUrl/"
).parsedSafe<Source>()
callback.invoke(
ExtractorLink(
this.name,
this.name,
source?.data ?: return,
"$mainUrl/",
Qualities.P1080.value,
INFER_TYPE
)
)
subtitleCallback.invoke(
SubtitleFile(
source.sub?.substringBefore("|")?.toLanguage() ?: return,
source.sub.substringAfter("|"),
)
)
}
private fun caesarShift(str: String, amount: Int): String {
var output = ""
val adjustedAmount = if (amount < 0) amount + 26 else amount
for (element in str) {
var c = element
if (c.isLetter()) {
val code = c.code
c = when (code) {
in 65..90 -> ((code - 65 + adjustedAmount) % 26 + 65).toChar()
in 97..122 -> ((code - 97 + adjustedAmount) % 26 + 97).toChar()
else -> c
}
}
output += c
}
return output
}
private fun mahoa(input: String, key: String): String {
val a = CryptoJS.encrypt(key, input)
return a.replace("U2FsdGVkX1", "")
.replace("/", "|a")
.replace("+", "|b")
.replace("=", "|c")
.replace("|", "-z")
}
private fun md5(input: String): String {
val md = MessageDigest.getInstance("MD5")
return BigInteger(1, md.digest(input.toByteArray())).toString(16).padStart(32, '0')
}
private fun String.toHex(): String {
return this.toByteArray().joinToString("") { "%02x".format(it) }
}
private fun String.findIn(data: String): String {
return this.toRegex().find(data)?.groupValues?.get(1) ?: ""
}
private fun String.toLanguage() : String {
return if(this == "EN") "English" else this
}
data class Source(
@JsonProperty("data") val data: String? = null,
@JsonProperty("sub") val sub: String? = null,
)
}
class TravelR : GMPlayer() {
override val name = "TravelR"
override val mainUrl = "https://travel-russia.xyz"
}
class Mwish : Filesim() {
override val name = "Mwish"
override var mainUrl = "https://mwish.pro"
}
class Animefever : Filesim() {
override val name = "Animefever"
override var mainUrl = "https://animefever.fun"
}
class Multimovies : Filesim() {
override val name = "Multimovies"
override var mainUrl = "https://multimovies.cloud"
}
class MultimoviesSB : StreamSB() {
override var name = "Multimovies"
override var mainUrl = "https://multimovies.website"
}
class Yipsu : Voe() {
override val name = "Yipsu"
override var mainUrl = "https://yip.su"
}

View file

@ -9,6 +9,7 @@ import com.lagradost.cloudstream3.extractors.Filesim
import com.lagradost.cloudstream3.extractors.GMPlayer import com.lagradost.cloudstream3.extractors.GMPlayer
import com.lagradost.cloudstream3.extractors.StreamSB import com.lagradost.cloudstream3.extractors.StreamSB
import com.lagradost.cloudstream3.extractors.Voe import com.lagradost.cloudstream3.extractors.Voe
import com.lagradost.cloudstream3.extractors.helper.AesHelper.cryptoAESHandler
import com.lagradost.cloudstream3.extractors.helper.GogoHelper import com.lagradost.cloudstream3.extractors.helper.GogoHelper
import com.lagradost.cloudstream3.network.CloudflareKiller import com.lagradost.cloudstream3.network.CloudflareKiller
import com.lagradost.nicehttp.RequestBodyTypes import com.lagradost.nicehttp.RequestBodyTypes
@ -126,7 +127,7 @@ object SoraExtractor : SoraStream() {
link.url, link.url,
link.referer, link.referer,
if (link.name == "VidSrc") Qualities.P1080.value else link.quality, if (link.name == "VidSrc") Qualities.P1080.value else link.quality,
link.isM3u8, link.type,
link.headers, link.headers,
link.extractorData link.extractorData
) )
@ -272,7 +273,7 @@ object SoraExtractor : SoraStream() {
video.url, video.url,
video.referer, video.referer,
Qualities.P1080.value, Qualities.P1080.value,
video.isM3u8, video.type,
video.headers, video.headers,
video.extractorData video.extractorData
) )
@ -414,7 +415,7 @@ object SoraExtractor : SoraStream() {
} else { } else {
"$idlixAPI/episode/$fixTitle-season-$season-episode-$episode" "$idlixAPI/episode/$fixTitle-season-$season-episode-$episode"
} }
invokeWpmovies(url, subtitleCallback, callback) invokeWpmovies(url, subtitleCallback, callback, encrypt = true)
} }
suspend fun invokeMultimovies( suspend fun invokeMultimovies(
@ -455,8 +456,14 @@ object SoraExtractor : SoraStream() {
subtitleCallback: (SubtitleFile) -> Unit, subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit, callback: (ExtractorLink) -> Unit,
fixIframe: Boolean = false, fixIframe: Boolean = false,
encrypt: Boolean = false,
key: String? = null,
) { ) {
val res = session.get(url ?: return) fun String.fixBloat() : String {
return this.replace("\"", "").replace("\\", "")
}
val res = app.get(url ?: return)
val headers = mapOf("X-Requested-With" to "XMLHttpRequest")
val referer = getBaseUrl(res.url) val referer = getBaseUrl(res.url)
val document = res.document val document = res.document
document.select("ul#playeroptionsul > li").map { document.select("ul#playeroptionsul > li").map {
@ -466,13 +473,17 @@ object SoraExtractor : SoraStream() {
it.attr("data-type") it.attr("data-type")
) )
}.apmap { (id, nume, type) -> }.apmap { (id, nume, type) ->
val json = session.post( val json = app.post(
url = "$referer/wp-admin/admin-ajax.php", data = mapOf( url = "$referer/wp-admin/admin-ajax.php", data = mapOf(
"action" to "doo_player_ajax", "post" to id, "nume" to nume, "type" to type "action" to "doo_player_ajax", "post" to id, "nume" to nume, "type" to type
), headers = mapOf("X-Requested-With" to "XMLHttpRequest"), referer = url ), headers = headers, referer = url
) )
val source = tryParseJson<ResponseHash>(json.text)?.embed_url?.let { val source = tryParseJson<ResponseHash>(json.text)?.let {
if (fixIframe) Jsoup.parse(it).select("IFRAME").attr("SRC") else it when {
encrypt -> cryptoAESHandler(it.embed_url,(it.key ?: return@apmap).toByteArray(), false)?.fixBloat()
fixIframe -> Jsoup.parse(it.embed_url).select("IFRAME").attr("SRC")
else -> it.embed_url
}
} ?: return@apmap } ?: return@apmap
if (!source.contains("youtube")) { if (!source.contains("youtube")) {
loadExtractor(source, "$referer/", subtitleCallback, callback) loadExtractor(source, "$referer/", subtitleCallback, callback)
@ -700,10 +711,10 @@ object SoraExtractor : SoraStream() {
link.url, link.url,
link.referer, link.referer,
when { when {
link.isM3u8 -> link.quality link.type == ExtractorLinkType.M3U8 -> link.quality
else -> getQualityFromName(it.first) else -> getQualityFromName(it.first)
}, },
link.isM3u8, link.type,
link.headers, link.headers,
link.extractorData link.extractorData
) )
@ -1028,10 +1039,10 @@ object SoraExtractor : SoraStream() {
link.url, link.url,
link.referer, link.referer,
when { when {
link.isM3u8 -> link.quality link.type == ExtractorLinkType.M3U8 -> link.quality
else -> it.third else -> it.third
}, },
link.isM3u8, link.type,
link.headers, link.headers,
link.extractorData link.extractorData
) )
@ -1506,7 +1517,9 @@ object SoraExtractor : SoraStream() {
subtitleCallback: (SubtitleFile) -> Unit, subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit callback: (ExtractorLink) -> Unit
) { ) {
val res = app.get("$m4uhdAPI/search/${title.createSlug()}.html").document val req = app.get("$m4uhdAPI/search/${title.createSlug()}.html")
val referer = getBaseUrl(req.url)
val res = req.document
val scriptData = res.select("div.row div.item").map { val scriptData = res.select("div.row div.item").map {
Triple( Triple(
it.selectFirst("img.imagecover")?.attr("title"), it.selectFirst("img.imagecover")?.attr("title"),
@ -1527,7 +1540,7 @@ object SoraExtractor : SoraStream() {
} }
} }
val link = fixUrl(script?.third ?: return, m4uhdAPI) val link = fixUrl(script?.third ?: return, referer)
val request = app.get(link) val request = app.get(link)
var cookiesSet = request.headers.filter { it.first == "set-cookie" } var cookiesSet = request.headers.filter { it.first == "set-cookie" }
var xsrf = var xsrf =
@ -1547,7 +1560,7 @@ object SoraExtractor : SoraStream() {
?: return ?: return
val idepisode = episodeData.select("button").attr("idepisode") ?: return val idepisode = episodeData.select("button").attr("idepisode") ?: return
val requestEmbed = app.post( val requestEmbed = app.post(
"$m4uhdAPI/ajaxtv", data = mapOf( "$referer/ajaxtv", data = mapOf(
"idepisode" to idepisode, "_token" to "$token" "idepisode" to idepisode, "_token" to "$token"
), referer = link, headers = mapOf( ), referer = link, headers = mapOf(
"X-Requested-With" to "XMLHttpRequest", "X-Requested-With" to "XMLHttpRequest",
@ -1561,14 +1574,16 @@ object SoraExtractor : SoraStream() {
cookiesSet.find { it.second.contains("XSRF-TOKEN") }?.second?.substringAfter("XSRF-TOKEN=") cookiesSet.find { it.second.contains("XSRF-TOKEN") }?.second?.substringAfter("XSRF-TOKEN=")
?.substringBefore(";") ?.substringBefore(";")
session = session =
cookiesSet.find { it.second.contains("laravel_session") }?.second?.substringAfter("laravel_session=") cookiesSet.find { it.second.contains("laravel_session") }?.second?.substringAfter(
"laravel_session="
)
?.substringBefore(";") ?.substringBefore(";")
requestEmbed.document.select("div.le-server span").map { it.attr("data") } requestEmbed.document.select("div.le-server span").map { it.attr("data") }
} }
m4uData.apmap { data -> m4uData.apmap { data ->
val iframe = app.post( val iframe = app.post(
"$m4uhdAPI/ajax", "$referer/ajax",
data = mapOf( data = mapOf(
"m4u" to data, "_token" to "$token" "m4u" to data, "_token" to "$token"
), ),
@ -1583,7 +1598,7 @@ object SoraExtractor : SoraStream() {
), ),
).document.select("iframe").attr("src") ).document.select("iframe").attr("src")
loadExtractor(iframe, m4uhdAPI, subtitleCallback, callback) loadExtractor(iframe, referer, subtitleCallback, callback)
} }
} }
@ -2540,80 +2555,6 @@ object SoraExtractor : SoraStream() {
} }
suspend fun invokePutlocker(
title: String? = null,
year: Int? = null,
season: Int? = null,
episode: Int? = null,
callback: (ExtractorLink) -> Unit,
) {
val query = if (season == null) {
title
} else {
"$title - season $season"
}
val res = app.get("$putlockerAPI/movie/search/$query").document
val scripData = res.select("div.movies-list div.ml-item").map {
it.selectFirst("h2")?.text() to it.selectFirst("a")?.attr("href")
}
val script = if (scripData.size == 1) {
scripData.first()
} else {
scripData.find {
if (season == null) {
it.first.equals(title, true) || (it.first?.contains(
"$title", true
) == true && it.first?.contains("$year") == true)
} else {
it.first?.contains("$title", true) == true && it.first?.contains(
"Season $season", true
) == true
}
}
}
val id = fixUrl(script?.second ?: return).split("-").lastOrNull()?.removeSuffix("/")
val iframe = app.get("$putlockerAPI/ajax/movie_episodes/$id")
.parsedSafe<PutlockerEpisodes>()?.html?.let { Jsoup.parse(it) }?.let { server ->
if (season == null) {
server.select("div.les-content a").map {
it.attr("data-id") to it.attr("data-server")
}
} else {
server.select("div.les-content a").map { it }
.filter { it.text().contains("Episode $episode", true) }.map {
it.attr("data-id") to it.attr("data-server")
}
}
}
iframe?.apmap {
delay(3000)
val embedUrl = app.get("$putlockerAPI/ajax/movie_embed/${it.first}")
.parsedSafe<PutlockerEmbed>()?.src ?: return@apmap null
val sources = extractPutlockerSources(embedUrl)?.parsedSafe<PutlockerResponses>()
argamap(
{
sources?.callback(embedUrl, "Server ${it.second}", callback)
},
{
if (!sources?.backupLink.isNullOrBlank()) {
extractPutlockerSources(sources?.backupLink)?.parsedSafe<PutlockerResponses>()
?.callback(
embedUrl, "Backup ${it.second}", callback
)
} else {
return@argamap
}
},
)
}
}
suspend fun invokeCryMovies( suspend fun invokeCryMovies(
imdbId: String? = null, imdbId: String? = null,
title: String? = null, title: String? = null,
@ -2884,36 +2825,84 @@ object SoraExtractor : SoraStream() {
} }
suspend fun invokeSusflix(
tmdbId: Int? = null,
season: Int? = null,
episode: Int? = null,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit,
) {
val url = if(season == null) {
"$susflixAPI/view/movie/$tmdbId"
} else {
"$susflixAPI/view/tv/$tmdbId/$season/$episode"
}
val res = app.get(url,cookies = mapOf(
"session" to "eyJfZnJlc2giOmZhbHNlLCJwaG9uZV9udW1iZXIiOiJzdXNoZXg5OCJ9.ZO6CsA.XUs6Y5gna8ExAUX55-myMi1QpYU"
)).text.substringAfter("response = {").substringBefore("};").replace("\'", "\"")
val sources = tryParseJson<SusflixSources>("{$res}")
sources?.qualities?.map { source ->
callback.invoke(
ExtractorLink(
"Susflix",
"Susflix",
source.path ?: return@map,
"$susflixAPI/",
getQualityFromName(source.quality)
)
)
}
sources?.srtfiles?.map { sub ->
subtitleCallback.invoke(
SubtitleFile(
sub.caption ?: return@map,
sub.url ?: return@map,
)
)
}
}
suspend fun invokeJump1(
tmdbId: Int? = null,
tvdbId: Int? = null,
title: String? = null,
year: Int? = null,
season: Int? = null,
episode: Int? = null,
callback: (ExtractorLink) -> Unit,
) {
val referer = "https://jump1.net/"
val res = if(season == null) {
val body = """{"filters":[{"type":"slug","args":{"slugs":["${title.createSlug()}-$year"]}}],"sort":"addedRecent","skip":0,"limit":100}""".toRequestBody(RequestBodyTypes.JSON.toMediaTypeOrNull())
app.post("$jump1API/api/movies", requestBody = body, referer = referer)
} else {
app.get("$jump1API/api/shows/$tvdbId/seasons", referer = referer)
}.text
val source = if(season == null) {
tryParseJson<Jump1Movies>(res)?.movies?.find { it.id == tmdbId }?.videoId
} else {
val jumpSeason = tryParseJson<ArrayList<Jump1Season>>(res)?.find { it.seasonNumber == season }?.id
val seasonRes = app.get("$jump1API/api/shows/seasons/${jumpSeason ?: return}/episodes", referer = referer)
tryParseJson<ArrayList<Jump1Episodes>>(seasonRes.text)?.find { it.episodeNumber == episode }?.videoId
}
callback.invoke(
ExtractorLink(
"Jump1",
"Jump1",
"$jump1API/hls/${source ?: return}/master.m3u8?ts=${APIHolder.unixTimeMS}",
referer,
Qualities.P1080.value,
true
)
)
}
} }
class TravelR : GMPlayer() {
override val name = "TravelR"
override val mainUrl = "https://travel-russia.xyz"
}
class Mwish : Filesim() {
override val name = "Mwish"
override var mainUrl = "https://mwish.pro"
}
class Animefever : Filesim() {
override val name = "Animefever"
override var mainUrl = "https://animefever.fun"
}
class Multimovies : Filesim() {
override val name = "Multimovies"
override var mainUrl = "https://multimovies.cloud"
}
class MultimoviesSB : StreamSB() {
override var name = "Multimovies"
override var mainUrl = "https://multimovies.website"
}
class Yipsu : Voe() {
override val name = "Yipsu"
override var mainUrl = "https://yip.su"
}

View file

@ -65,7 +65,8 @@ data class HdMovieBoxIframe(
data class ResponseHash( data class ResponseHash(
@JsonProperty("embed_url") val embed_url: String, @JsonProperty("embed_url") val embed_url: String,
@JsonProperty("type") val type: String?, @JsonProperty("key") val key: String? = null,
@JsonProperty("type") val type: String? = null,
) )
data class KisskhSources( data class KisskhSources(
@ -87,11 +88,41 @@ data class KisskhDetail(
@JsonProperty("episodes") val episodes: ArrayList<KisskhEpisodes>? = arrayListOf(), @JsonProperty("episodes") val episodes: ArrayList<KisskhEpisodes>? = arrayListOf(),
) )
data class SusflixSrtfiles(
@JsonProperty("caption") val caption: String? = null,
@JsonProperty("url") val url: String? = null,
)
data class SusflixQualities(
@JsonProperty("path") val path: String? = null,
@JsonProperty("quality") val quality: String? = null,
)
data class SusflixSources(
@JsonProperty("Qualities") val qualities: ArrayList<SusflixQualities>? = arrayListOf(),
@JsonProperty("Srtfiles") val srtfiles: ArrayList<SusflixSrtfiles>? = arrayListOf(),
)
data class KisskhResults( data class KisskhResults(
@JsonProperty("id") val id: Int?, @JsonProperty("id") val id: Int?,
@JsonProperty("title") val title: String?, @JsonProperty("title") val title: String?,
) )
data class Jump1Episodes(
@JsonProperty("id") val id: Any? = null,
@JsonProperty("episodeNumber") val episodeNumber: Int? = null,
@JsonProperty("videoId") val videoId: String? = null,
)
data class Jump1Season(
@JsonProperty("seasonNumber") val seasonNumber: Int? = null,
@JsonProperty("id") val id: String? = null,
)
data class Jump1Movies(
@JsonProperty("movies") val movies: ArrayList<Jump1Episodes>? = arrayListOf(),
)
data class EpisodesFwatayako( data class EpisodesFwatayako(
@JsonProperty("id") val id: String? = null, @JsonProperty("id") val id: String? = null,
@JsonProperty("file") val file: String? = null, @JsonProperty("file") val file: String? = null,
@ -206,25 +237,6 @@ data class WatchOnlineResponse(
@JsonProperty("subtitles") val subtitles: Any? = null, @JsonProperty("subtitles") val subtitles: Any? = null,
) )
data class PutlockerEpisodes(
@JsonProperty("html") val html: String? = null,
)
data class PutlockerEmbed(
@JsonProperty("src") val src: String? = null,
)
data class PutlockerSources(
@JsonProperty("file") val file: String,
@JsonProperty("label") val label: String? = null,
@JsonProperty("type") val type: String? = null,
)
data class PutlockerResponses(
@JsonProperty("sources") val sources: ArrayList<PutlockerSources>? = arrayListOf(),
@JsonProperty("backupLink") val backupLink: String? = null,
)
data class CryMoviesProxyHeaders( data class CryMoviesProxyHeaders(
@JsonProperty("request") val request: Map<String, String>?, @JsonProperty("request") val request: Map<String, String>?,
) )

View file

@ -34,7 +34,6 @@ import com.hexated.SoraExtractor.invokeMoviezAdd
import com.hexated.SoraExtractor.invokeNavy import com.hexated.SoraExtractor.invokeNavy
import com.hexated.SoraExtractor.invokeNinetv import com.hexated.SoraExtractor.invokeNinetv
import com.hexated.SoraExtractor.invokeNowTv import com.hexated.SoraExtractor.invokeNowTv
import com.hexated.SoraExtractor.invokePutlocker
import com.hexated.SoraExtractor.invokeRStream import com.hexated.SoraExtractor.invokeRStream
import com.hexated.SoraExtractor.invokeRidomovies import com.hexated.SoraExtractor.invokeRidomovies
import com.hexated.SoraExtractor.invokeShinobiMovies import com.hexated.SoraExtractor.invokeShinobiMovies
@ -42,11 +41,13 @@ import com.hexated.SoraExtractor.invokeSmashyStream
import com.hexated.SoraExtractor.invokeDumpStream import com.hexated.SoraExtractor.invokeDumpStream
import com.hexated.SoraExtractor.invokeEmovies import com.hexated.SoraExtractor.invokeEmovies
import com.hexated.SoraExtractor.invokeFourCartoon import com.hexated.SoraExtractor.invokeFourCartoon
import com.hexated.SoraExtractor.invokeJump1
import com.hexated.SoraExtractor.invokeMoment import com.hexated.SoraExtractor.invokeMoment
import com.hexated.SoraExtractor.invokeMultimovies import com.hexated.SoraExtractor.invokeMultimovies
import com.hexated.SoraExtractor.invokeNetmovies import com.hexated.SoraExtractor.invokeNetmovies
import com.hexated.SoraExtractor.invokePobmovies import com.hexated.SoraExtractor.invokePobmovies
import com.hexated.SoraExtractor.invokePrimewire import com.hexated.SoraExtractor.invokePrimewire
import com.hexated.SoraExtractor.invokeSusflix
import com.hexated.SoraExtractor.invokeTvMovies import com.hexated.SoraExtractor.invokeTvMovies
import com.hexated.SoraExtractor.invokeUhdmovies import com.hexated.SoraExtractor.invokeUhdmovies
import com.hexated.SoraExtractor.invokeVidsrcto import com.hexated.SoraExtractor.invokeVidsrcto
@ -55,7 +56,6 @@ import com.hexated.SoraExtractor.invokeWatchsomuch
import com.lagradost.cloudstream3.LoadResponse.Companion.addImdbId import com.lagradost.cloudstream3.LoadResponse.Companion.addImdbId
import com.lagradost.cloudstream3.LoadResponse.Companion.addTMDbId import com.lagradost.cloudstream3.LoadResponse.Companion.addTMDbId
import com.lagradost.cloudstream3.extractors.VidSrcExtractor import com.lagradost.cloudstream3.extractors.VidSrcExtractor
import com.lagradost.cloudstream3.syncproviders.SyncIdName
import com.lagradost.cloudstream3.utils.AppUtils.parseJson import com.lagradost.cloudstream3.utils.AppUtils.parseJson
import com.lagradost.cloudstream3.utils.AppUtils.toJson import com.lagradost.cloudstream3.utils.AppUtils.toJson
import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.ExtractorLink
@ -94,7 +94,7 @@ open class SoraStream : TmdbProvider() {
const val hdMovieBoxAPI = "https://hdmoviebox.net" const val hdMovieBoxAPI = "https://hdmoviebox.net"
const val dreamfilmAPI = "https://dreamfilmsw.net" const val dreamfilmAPI = "https://dreamfilmsw.net"
const val series9API = "https://series9.cx" const val series9API = "https://series9.cx"
const val idlixAPI = "https://tv.idlixprime.com" const val idlixAPI = "https://tv.idlixplus.net"
const val noverseAPI = "https://www.nollyverse.com" const val noverseAPI = "https://www.nollyverse.com"
const val filmxyAPI = "https://www.filmxy.vip" const val filmxyAPI = "https://www.filmxy.vip"
const val kimcartoonAPI = "https://kimcartoon.li" const val kimcartoonAPI = "https://kimcartoon.li"
@ -102,7 +102,7 @@ open class SoraStream : TmdbProvider() {
const val crunchyrollAPI = "https://beta-api.crunchyroll.com" const val crunchyrollAPI = "https://beta-api.crunchyroll.com"
const val kissKhAPI = "https://kisskh.co" const val kissKhAPI = "https://kisskh.co"
const val lingAPI = "https://ling-online.net" const val lingAPI = "https://ling-online.net"
const val uhdmoviesAPI = "https://uhdmovies.actor" const val uhdmoviesAPI = "https://uhdmovies.wiki"
const val fwatayakoAPI = "https://5100.svetacdn.in" const val fwatayakoAPI = "https://5100.svetacdn.in"
const val gMoviesAPI = "https://gdrivemovies.xyz" const val gMoviesAPI = "https://gdrivemovies.xyz"
const val fdMoviesAPI = "https://freedrivemovie.lol" const val fdMoviesAPI = "https://freedrivemovie.lol"
@ -118,7 +118,6 @@ open class SoraStream : TmdbProvider() {
const val ask4MoviesAPI = "https://ask4movie.nl" const val ask4MoviesAPI = "https://ask4movie.nl"
const val watchOnlineAPI = "https://watchonline.ag" const val watchOnlineAPI = "https://watchonline.ag"
const val nineTvAPI = "https://moviesapi.club" const val nineTvAPI = "https://moviesapi.club"
const val putlockerAPI = "https://ww7.putlocker.vip"
const val fmoviesAPI = "https://fmovies.to" const val fmoviesAPI = "https://fmovies.to"
const val nowTvAPI = "https://myfilestorage.xyz" const val nowTvAPI = "https://myfilestorage.xyz"
const val gokuAPI = "https://goku.sx" const val gokuAPI = "https://goku.sx"
@ -127,7 +126,7 @@ open class SoraStream : TmdbProvider() {
const val emoviesAPI = "https://emovies.si" const val emoviesAPI = "https://emovies.si"
const val pobmoviesAPI = "https://pobmovies.cam" const val pobmoviesAPI = "https://pobmovies.cam"
const val fourCartoonAPI = "https://4cartoon.net" const val fourCartoonAPI = "https://4cartoon.net"
const val multimoviesAPI = "https://multimovies.xyz" const val multimoviesAPI = "https://multi-movies.xyz"
const val netmoviesAPI = "https://netmovies.to" const val netmoviesAPI = "https://netmovies.to"
const val momentAPI = "https://moment-explanation-i-244.site" const val momentAPI = "https://moment-explanation-i-244.site"
const val doomoviesAPI = "https://doomovies.net" const val doomoviesAPI = "https://doomovies.net"
@ -135,6 +134,8 @@ open class SoraStream : TmdbProvider() {
const val vidsrctoAPI = "https://vidsrc.to" const val vidsrctoAPI = "https://vidsrc.to"
const val dramadayAPI = "https://dramaday.me" const val dramadayAPI = "https://dramaday.me"
const val animetoshoAPI = "https://animetosho.org" const val animetoshoAPI = "https://animetosho.org"
const val susflixAPI = "https://susflix.tv"
const val jump1API = "https://ca.jump1.net"
// INDEX SITE // INDEX SITE
const val dahmerMoviesAPI = "https://edytjedhgmdhm.abfhaqrhbnf.workers.dev" const val dahmerMoviesAPI = "https://edytjedhgmdhm.abfhaqrhbnf.workers.dev"
@ -279,6 +280,7 @@ open class SoraStream : TmdbProvider() {
LinkData( LinkData(
data.id, data.id,
res.external_ids?.imdb_id, res.external_ids?.imdb_id,
res.external_ids?.tvdb_id,
data.type, data.type,
eps.seasonNumber, eps.seasonNumber,
eps.episodeNumber, eps.episodeNumber,
@ -332,6 +334,7 @@ open class SoraStream : TmdbProvider() {
LinkData( LinkData(
data.id, data.id,
res.external_ids?.imdb_id, res.external_ids?.imdb_id,
res.external_ids?.tvdb_id,
data.type, data.type,
title = title, title = title,
year = year, year = year,
@ -536,7 +539,7 @@ open class SoraStream : TmdbProvider() {
) )
}, },
{ {
invokeM4uhd( if(!res.isAnime) invokeM4uhd(
res.title, res.title,
res.year, res.year,
res.season, res.season,
@ -545,9 +548,6 @@ open class SoraStream : TmdbProvider() {
callback callback
) )
}, },
{
invokePutlocker(res.title, res.year, res.season, res.episode, callback)
},
{ {
invokeTvMovies(res.title, res.season, res.episode, callback) invokeTvMovies(res.title, res.season, res.episode, callback)
}, },
@ -746,6 +746,20 @@ open class SoraStream : TmdbProvider() {
{ {
if(!res.isAnime) invoke2embed(res.imdbId,res.season,res.episode,callback) if(!res.isAnime) invoke2embed(res.imdbId,res.season,res.episode,callback)
}, },
// {
// invokeSusflix(res.id,res.season,res.episode,subtitleCallback,callback)
// },
{
if(!res.isAnime) invokeJump1(
res.id,
res.tvdbId,
res.title,
res.year,
res.season,
res.episode,
callback
)
},
) )
return true return true
@ -754,6 +768,7 @@ open class SoraStream : TmdbProvider() {
data class LinkData( data class LinkData(
val id: Int? = null, val id: Int? = null,
val imdbId: String? = null, val imdbId: String? = null,
val tvdbId: Int? = null,
val type: String? = null, val type: String? = null,
val season: Int? = null, val season: Int? = null,
val episode: Int? = null, val episode: Int? = null,
@ -858,7 +873,7 @@ open class SoraStream : TmdbProvider() {
data class ExternalIds( data class ExternalIds(
@JsonProperty("imdb_id") val imdb_id: String? = null, @JsonProperty("imdb_id") val imdb_id: String? = null,
@JsonProperty("tvdb_id") val tvdb_id: String? = null, @JsonProperty("tvdb_id") val tvdb_id: Int? = null,
) )
data class Credits( data class Credits(

View file

@ -21,7 +21,6 @@ import com.hexated.SoraExtractor.invokeMovieHab
import com.hexated.SoraExtractor.invokeNavy import com.hexated.SoraExtractor.invokeNavy
import com.hexated.SoraExtractor.invokeNinetv import com.hexated.SoraExtractor.invokeNinetv
import com.hexated.SoraExtractor.invokeNowTv import com.hexated.SoraExtractor.invokeNowTv
import com.hexated.SoraExtractor.invokePutlocker
import com.hexated.SoraExtractor.invokeRStream import com.hexated.SoraExtractor.invokeRStream
import com.hexated.SoraExtractor.invokeRidomovies import com.hexated.SoraExtractor.invokeRidomovies
import com.hexated.SoraExtractor.invokeSeries9 import com.hexated.SoraExtractor.invokeSeries9
@ -29,10 +28,12 @@ import com.hexated.SoraExtractor.invokeSmashyStream
import com.hexated.SoraExtractor.invokeDumpStream import com.hexated.SoraExtractor.invokeDumpStream
import com.hexated.SoraExtractor.invokeEmovies import com.hexated.SoraExtractor.invokeEmovies
import com.hexated.SoraExtractor.invokeFourCartoon import com.hexated.SoraExtractor.invokeFourCartoon
import com.hexated.SoraExtractor.invokeJump1
import com.hexated.SoraExtractor.invokeMoment import com.hexated.SoraExtractor.invokeMoment
import com.hexated.SoraExtractor.invokeMultimovies import com.hexated.SoraExtractor.invokeMultimovies
import com.hexated.SoraExtractor.invokeNetmovies import com.hexated.SoraExtractor.invokeNetmovies
import com.hexated.SoraExtractor.invokePrimewire import com.hexated.SoraExtractor.invokePrimewire
import com.hexated.SoraExtractor.invokeSusflix
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.invokeWatchOnline
@ -56,14 +57,17 @@ class SoraStreamLite : SoraStream() {
argamap( argamap(
{ {
invokePutlocker( if(!res.isAnime) invokeJump1(res.id,res.tvdbId,res.title,res.year,res.season,res.episode,callback)
res.title,
res.year,
res.season,
res.episode,
callback
)
}, },
// {
// invokeSusflix(
// res.id,
// res.season,
// res.episode,
// subtitleCallback,
// callback
// )
// },
{ {
invokeWatchsomuch( invokeWatchsomuch(
res.imdbId, res.imdbId,
@ -253,7 +257,7 @@ class SoraStreamLite : SoraStream() {
invokeFwatayako(res.imdbId, res.season, res.episode, callback) invokeFwatayako(res.imdbId, res.season, res.episode, callback)
}, },
{ {
invokeM4uhd( if(!res.isAnime) invokeM4uhd(
res.title, res.title,
res.year, res.year,
res.season, res.season,

View file

@ -17,5 +17,6 @@ class SoraStreamPlugin: Plugin() {
registerExtractorAPI(Yipsu()) registerExtractorAPI(Yipsu())
registerExtractorAPI(Mwish()) registerExtractorAPI(Mwish())
registerExtractorAPI(TravelR()) registerExtractorAPI(TravelR())
registerExtractorAPI(Playm4u())
} }
} }

View file

@ -10,7 +10,6 @@ import com.hexated.SoraStream.Companion.filmxyAPI
import com.hexated.SoraStream.Companion.fmoviesAPI import com.hexated.SoraStream.Companion.fmoviesAPI
import com.hexated.SoraStream.Companion.gdbot import com.hexated.SoraStream.Companion.gdbot
import com.hexated.SoraStream.Companion.malsyncAPI import com.hexated.SoraStream.Companion.malsyncAPI
import com.hexated.SoraStream.Companion.putlockerAPI
import com.hexated.SoraStream.Companion.smashyStreamAPI import com.hexated.SoraStream.Companion.smashyStreamAPI
import com.hexated.SoraStream.Companion.tvMoviesAPI import com.hexated.SoraStream.Companion.tvMoviesAPI
import com.hexated.SoraStream.Companion.watchOnlineAPI import com.hexated.SoraStream.Companion.watchOnlineAPI
@ -45,7 +44,6 @@ import kotlin.math.min
val bflixChipperKey = base64DecodeAPI("Yjc=ejM=TzA=YTk=WHE=WnU=bXU=RFo=") val bflixChipperKey = base64DecodeAPI("Yjc=ejM=TzA=YTk=WHE=WnU=bXU=RFo=")
const val bflixKey = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" const val bflixKey = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
const val otakuzBaseUrl = "https://otakuz.live/"
val encodedIndex = arrayOf( val encodedIndex = arrayOf(
"GamMovies", "GamMovies",
"JSMovies", "JSMovies",
@ -1055,52 +1053,6 @@ suspend fun getCrunchyrollIdFromMalSync(aniId: String?): String? {
?: regex.find("$crunchyroll")?.groupValues?.getOrNull(1) ?: regex.find("$crunchyroll")?.groupValues?.getOrNull(1)
} }
suspend fun extractPutlockerSources(url: String?): NiceResponse? {
val embedHost = url?.substringBefore("/embed-player")
val player = app.get(
url ?: return null,
referer = "${putlockerAPI}/"
).document.select("div#player")
val text = "\"${player.attr("data-id")}\""
val password = player.attr("data-hash")
val cipher = CryptoAES.plEncrypt(password, text)
return app.get(
"$embedHost/ajax/getSources/", params = mapOf(
"id" to cipher.cipherText,
"h" to cipher.password,
"a" to cipher.iv,
"t" to cipher.salt,
), referer = url
)
}
suspend fun PutlockerResponses?.callback(
referer: String,
server: String,
callback: (ExtractorLink) -> Unit
) {
val ref = getBaseUrl(referer)
this?.sources?.map { source ->
val request = app.get(source.file, referer = ref)
callback.invoke(
ExtractorLink(
"Putlocker [$server]",
"Putlocker [$server]",
if (!request.isSuccessful) return@map null else source.file,
ref,
if (source.file.contains("m3u8")) getPutlockerQuality(request.text) else source.label?.replace(
Regex("[Pp]"),
""
)?.trim()?.toIntOrNull()
?: Qualities.P720.value,
source.file.contains("m3u8")
)
)
}
}
suspend fun convertTmdbToAnimeId( suspend fun convertTmdbToAnimeId(
title: String?, title: String?,
date: String?, date: String?,
@ -1191,10 +1143,10 @@ suspend fun loadCustomExtractor(
link.url, link.url,
link.referer, link.referer,
when { when {
link.isM3u8 -> link.quality link.type == ExtractorLinkType.M3U8 -> link.quality
else -> quality ?: link.quality else -> quality ?: link.quality
}, },
link.isM3u8, link.type,
link.headers, link.headers,
link.extractorData link.extractorData
) )
@ -1655,161 +1607,6 @@ private enum class Symbol(val decimalValue: Int) {
} }
} }
// code found on https://stackoverflow.com/a/63701411
/**
* Conforming with CryptoJS AES method
*/
// see https://gist.github.com/thackerronak/554c985c3001b16810af5fc0eb5c358f
@Suppress("unused", "FunctionName", "SameParameterValue")
object CryptoAES {
private const val KEY_SIZE = 256
private const val IV_SIZE = 128
private const val HASH_CIPHER = "AES/CBC/PKCS5Padding"
private const val AES = "AES"
private const val KDF_DIGEST = "MD5"
// Seriously crypto-js, what's wrong with you?
private const val APPEND = "Salted__"
/**
* Encrypt
* @param password passphrase
* @param plainText plain string
*/
fun encrypt(password: String, plainText: String): String {
val saltBytes = generateSalt(8)
val key = ByteArray(KEY_SIZE / 8)
val iv = ByteArray(IV_SIZE / 8)
EvpKDF(password.toByteArray(), KEY_SIZE, IV_SIZE, saltBytes, key, iv)
val keyS = SecretKeySpec(key, AES)
val cipher = Cipher.getInstance(HASH_CIPHER)
val ivSpec = IvParameterSpec(iv)
cipher.init(Cipher.ENCRYPT_MODE, keyS, ivSpec)
val cipherText = cipher.doFinal(plainText.toByteArray())
// Thanks kientux for this: https://gist.github.com/kientux/bb48259c6f2133e628ad
// Create CryptoJS-like encrypted!
val sBytes = APPEND.toByteArray()
val b = ByteArray(sBytes.size + saltBytes.size + cipherText.size)
System.arraycopy(sBytes, 0, b, 0, sBytes.size)
System.arraycopy(saltBytes, 0, b, sBytes.size, saltBytes.size)
System.arraycopy(cipherText, 0, b, sBytes.size + saltBytes.size, cipherText.size)
val bEncode = Base64.encode(b, Base64.NO_WRAP)
return String(bEncode)
}
fun plEncrypt(password: String, plainText: String): EncryptResult {
val saltBytes = generateSalt(8)
val key = ByteArray(KEY_SIZE / 8)
val iv = ByteArray(IV_SIZE / 8)
EvpKDF(password.toByteArray(), KEY_SIZE, IV_SIZE, saltBytes, key, iv)
val keyS = SecretKeySpec(key, AES)
val cipher = Cipher.getInstance(HASH_CIPHER)
val ivSpec = IvParameterSpec(iv)
cipher.init(Cipher.ENCRYPT_MODE, keyS, ivSpec)
val cipherText = cipher.doFinal(plainText.toByteArray())
val bEncode = Base64.encode(cipherText, Base64.NO_WRAP)
return EncryptResult(
String(bEncode).toHex(),
password.toHex(),
saltBytes.toHex(),
iv.toHex()
)
}
/**
* Decrypt
* Thanks Artjom B. for this: http://stackoverflow.com/a/29152379/4405051
* @param password passphrase
* @param cipherText encrypted string
*/
fun decrypt(password: String, cipherText: String): String {
val ctBytes = Base64.decode(cipherText.toByteArray(), Base64.NO_WRAP)
val saltBytes = Arrays.copyOfRange(ctBytes, 8, 16)
val cipherTextBytes = Arrays.copyOfRange(ctBytes, 16, ctBytes.size)
val key = ByteArray(KEY_SIZE / 8)
val iv = ByteArray(IV_SIZE / 8)
EvpKDF(password.toByteArray(), KEY_SIZE, IV_SIZE, saltBytes, key, iv)
val cipher = Cipher.getInstance(HASH_CIPHER)
val keyS = SecretKeySpec(key, AES)
cipher.init(Cipher.DECRYPT_MODE, keyS, IvParameterSpec(iv))
val plainText = cipher.doFinal(cipherTextBytes)
return String(plainText)
}
private fun EvpKDF(
password: ByteArray,
keySize: Int,
ivSize: Int,
salt: ByteArray,
resultKey: ByteArray,
resultIv: ByteArray
): ByteArray {
return EvpKDF(password, keySize, ivSize, salt, 1, KDF_DIGEST, resultKey, resultIv)
}
@Suppress("NAME_SHADOWING")
private fun EvpKDF(
password: ByteArray,
keySize: Int,
ivSize: Int,
salt: ByteArray,
iterations: Int,
hashAlgorithm: String,
resultKey: ByteArray,
resultIv: ByteArray
): ByteArray {
val keySize = keySize / 32
val ivSize = ivSize / 32
val targetKeySize = keySize + ivSize
val derivedBytes = ByteArray(targetKeySize * 4)
var numberOfDerivedWords = 0
var block: ByteArray? = null
val hash = MessageDigest.getInstance(hashAlgorithm)
while (numberOfDerivedWords < targetKeySize) {
if (block != null) {
hash.update(block)
}
hash.update(password)
block = hash.digest(salt)
hash.reset()
// Iterations
for (i in 1 until iterations) {
block = hash.digest(block!!)
hash.reset()
}
System.arraycopy(
block!!, 0, derivedBytes, numberOfDerivedWords * 4,
min(block.size, (targetKeySize - numberOfDerivedWords) * 4)
)
numberOfDerivedWords += block.size / 4
}
System.arraycopy(derivedBytes, 0, resultKey, 0, keySize * 4)
System.arraycopy(derivedBytes, keySize * 4, resultIv, 0, ivSize * 4)
return derivedBytes // key + iv
}
private fun generateSalt(length: Int): ByteArray {
return ByteArray(length).apply {
SecureRandom().nextBytes(this)
}
}
private fun ByteArray.toHex(): String =
joinToString(separator = "") { eachByte -> "%02x".format(eachByte) }
private fun String.toHex(): String = toByteArray().toHex()
data class EncryptResult(
val cipherText: String,
val password: String,
val salt: String,
val iv: String
)
}
object DumpUtils { object DumpUtils {
private val deviceId = getDeviceId() private val deviceId = getDeviceId()
@ -1927,3 +1724,126 @@ object RSAEncryptionHelper {
null null
} }
} }
// code found on https://stackoverflow.com/a/63701411
/**
* Conforming with CryptoJS AES method
*/
// see https://gist.github.com/thackerronak/554c985c3001b16810af5fc0eb5c358f
@Suppress("unused", "FunctionName", "SameParameterValue")
object CryptoJS {
private const val KEY_SIZE = 256
private const val IV_SIZE = 128
private const val HASH_CIPHER = "AES/CBC/PKCS7Padding"
private const val AES = "AES"
private const val KDF_DIGEST = "MD5"
// Seriously crypto-js, what's wrong with you?
private const val APPEND = "Salted__"
/**
* Encrypt
* @param password passphrase
* @param plainText plain string
*/
fun encrypt(password: String, plainText: String): String {
val saltBytes = generateSalt(8)
val key = ByteArray(KEY_SIZE / 8)
val iv = ByteArray(IV_SIZE / 8)
EvpKDF(password.toByteArray(), KEY_SIZE, IV_SIZE, saltBytes, key, iv)
val keyS = SecretKeySpec(key, AES)
val cipher = Cipher.getInstance(HASH_CIPHER)
val ivSpec = IvParameterSpec(iv)
cipher.init(Cipher.ENCRYPT_MODE, keyS, ivSpec)
val cipherText = cipher.doFinal(plainText.toByteArray())
// Thanks kientux for this: https://gist.github.com/kientux/bb48259c6f2133e628ad
// Create CryptoJS-like encrypted!
val sBytes = APPEND.toByteArray()
val b = ByteArray(sBytes.size + saltBytes.size + cipherText.size)
System.arraycopy(sBytes, 0, b, 0, sBytes.size)
System.arraycopy(saltBytes, 0, b, sBytes.size, saltBytes.size)
System.arraycopy(cipherText, 0, b, sBytes.size + saltBytes.size, cipherText.size)
val bEncode = Base64.encode(b, Base64.NO_WRAP)
return String(bEncode)
}
/**
* Decrypt
* Thanks Artjom B. for this: http://stackoverflow.com/a/29152379/4405051
* @param password passphrase
* @param cipherText encrypted string
*/
fun decrypt(password: String, cipherText: String): String {
val ctBytes = Base64.decode(cipherText.toByteArray(), Base64.NO_WRAP)
val saltBytes = Arrays.copyOfRange(ctBytes, 8, 16)
val cipherTextBytes = Arrays.copyOfRange(ctBytes, 16, ctBytes.size)
val key = ByteArray(KEY_SIZE / 8)
val iv = ByteArray(IV_SIZE / 8)
EvpKDF(password.toByteArray(), KEY_SIZE, IV_SIZE, saltBytes, key, iv)
val cipher = Cipher.getInstance(HASH_CIPHER)
val keyS = SecretKeySpec(key, AES)
cipher.init(Cipher.DECRYPT_MODE, keyS, IvParameterSpec(iv))
val plainText = cipher.doFinal(cipherTextBytes)
return String(plainText)
}
private fun EvpKDF(
password: ByteArray,
keySize: Int,
ivSize: Int,
salt: ByteArray,
resultKey: ByteArray,
resultIv: ByteArray
): ByteArray {
return EvpKDF(password, keySize, ivSize, salt, 1, KDF_DIGEST, resultKey, resultIv)
}
@Suppress("NAME_SHADOWING")
private fun EvpKDF(
password: ByteArray,
keySize: Int,
ivSize: Int,
salt: ByteArray,
iterations: Int,
hashAlgorithm: String,
resultKey: ByteArray,
resultIv: ByteArray
): ByteArray {
val keySize = keySize / 32
val ivSize = ivSize / 32
val targetKeySize = keySize + ivSize
val derivedBytes = ByteArray(targetKeySize * 4)
var numberOfDerivedWords = 0
var block: ByteArray? = null
val hash = MessageDigest.getInstance(hashAlgorithm)
while (numberOfDerivedWords < targetKeySize) {
if (block != null) {
hash.update(block)
}
hash.update(password)
block = hash.digest(salt)
hash.reset()
// Iterations
for (i in 1 until iterations) {
block = hash.digest(block!!)
hash.reset()
}
System.arraycopy(
block!!, 0, derivedBytes, numberOfDerivedWords * 4,
min(block.size, (targetKeySize - numberOfDerivedWords) * 4)
)
numberOfDerivedWords += block.size / 4
}
System.arraycopy(derivedBytes, 0, resultKey, 0, keySize * 4)
System.arraycopy(derivedBytes, keySize * 4, resultIv, 0, ivSize * 4)
return derivedBytes // key + iv
}
private fun generateSalt(length: Int): ByteArray {
return ByteArray(length).apply {
SecureRandom().nextBytes(this)
}
}
}

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.ltd" override var mainUrl = "https://yomovies.fan"
private var directUrl = "" private var directUrl = ""
override var name = "Yomovies" override var name = "Yomovies"
override val hasMainPage = true override val hasMainPage = true

View file

@ -2,7 +2,7 @@ rootProject.name = "CloudstreamPlugins"
// This file sets what projects are included. All new projects should get automatically included unless specified in "disabled" variable. // This file sets what projects are included. All new projects should get automatically included unless specified in "disabled" variable.
val disabled = listOf<String>("Animixplay") val disabled = listOf<String>("Animixplay","Kickassanime")
File(rootDir, ".").eachDir { dir -> File(rootDir, ".").eachDir { dir ->
if (!disabled.contains(dir.name) && File(dir, "build.gradle.kts").exists()) { if (!disabled.contains(dir.name) && File(dir, "build.gradle.kts").exists()) {