forked from recloudstream/cloudstream
small fix
This commit is contained in:
parent
4ac7e0c967
commit
cd569b44c8
7 changed files with 102 additions and 367 deletions
|
@ -17,6 +17,8 @@ class NineAnimeProvider : MainAPI() {
|
||||||
override val supportedTypes = setOf(TvType.Anime)
|
override val supportedTypes = setOf(TvType.Anime)
|
||||||
override val hasQuickSearch = true
|
override val hasQuickSearch = true
|
||||||
|
|
||||||
|
// taken from https://github.com/saikou-app/saikou/blob/b35364c8c2a00364178a472fccf1ab72f09815b4/app/src/main/java/ani/saikou/parsers/anime/NineAnime.kt
|
||||||
|
// GNU General Public License v3.0 https://github.com/saikou-app/saikou/blob/main/LICENSE.md
|
||||||
companion object {
|
companion object {
|
||||||
fun getDubStatus(title: String): DubStatus {
|
fun getDubStatus(title: String): DubStatus {
|
||||||
return if (title.contains("(dub)", ignoreCase = true)) {
|
return if (title.contains("(dub)", ignoreCase = true)) {
|
||||||
|
@ -31,17 +33,17 @@ class NineAnimeProvider : MainAPI() {
|
||||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
|
||||||
private const val cipherKey = "rTKp3auwu0ULA6II"
|
private const val cipherKey = "rTKp3auwu0ULA6II"
|
||||||
|
|
||||||
private fun encodeVrf(text: String): String {
|
fun encodeVrf(text: String, mainKey: String): String {
|
||||||
return encode(
|
return encode(
|
||||||
encrypt(
|
encrypt(
|
||||||
cipher(cipherKey, encode(text)),
|
cipher(mainKey, encode(text)),
|
||||||
nineAnimeKey
|
nineAnimeKey
|
||||||
).replace("""=+$""".toRegex(), "")
|
).replace("""=+$""".toRegex(), "")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun decodeVrf(text: String): String {
|
fun decodeVrf(text: String, mainKey: String): String {
|
||||||
return decode(cipher(cipherKey, decrypt(text, nineAnimeKey)))
|
return decode(cipher(mainKey, decrypt(text, nineAnimeKey)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun encrypt(input: String, key: String): String {
|
fun encrypt(input: String, key: String): String {
|
||||||
|
@ -129,7 +131,7 @@ class NineAnimeProvider : MainAPI() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun encode(input: String): String =
|
fun encode(input: String): String =
|
||||||
java.net.URLEncoder.encode(input, "utf-8").replace("+", "%20")
|
java.net.URLEncoder.encode(input, "utf-8").replace("+", "%20")
|
||||||
|
|
||||||
private fun decode(input: String): String = java.net.URLDecoder.decode(input, "utf-8")
|
private fun decode(input: String): String = java.net.URLDecoder.decode(input, "utf-8")
|
||||||
|
@ -192,7 +194,7 @@ class NineAnimeProvider : MainAPI() {
|
||||||
)
|
)
|
||||||
|
|
||||||
override suspend fun quickSearch(query: String): List<SearchResponse>? {
|
override suspend fun quickSearch(query: String): List<SearchResponse>? {
|
||||||
val vrf = encodeVrf(query)
|
val vrf = encodeVrf(query, cipherKey)
|
||||||
val url =
|
val url =
|
||||||
"$mainUrl/ajax/anime/search?keyword=$query&vrf=$vrf"
|
"$mainUrl/ajax/anime/search?keyword=$query&vrf=$vrf"
|
||||||
val response = app.get(url).parsedSafe<QuickSearchResponse>()
|
val response = app.get(url).parsedSafe<QuickSearchResponse>()
|
||||||
|
@ -207,7 +209,7 @@ class NineAnimeProvider : MainAPI() {
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun search(query: String): List<SearchResponse> {
|
override suspend fun search(query: String): List<SearchResponse> {
|
||||||
val vrf = encodeVrf(query)
|
val vrf = encodeVrf(query, cipherKey)
|
||||||
//?language%5B%5D=${if (selectDub) "dubbed" else "subbed"}&
|
//?language%5B%5D=${if (selectDub) "dubbed" else "subbed"}&
|
||||||
val url =
|
val url =
|
||||||
"$mainUrl/filter?keyword=${encode(query)}&vrf=${vrf}&page=1"
|
"$mainUrl/filter?keyword=${encode(query)}&vrf=${vrf}&page=1"
|
||||||
|
@ -236,7 +238,8 @@ class NineAnimeProvider : MainAPI() {
|
||||||
?: throw ErrorLoadingException("Could not find title")
|
?: throw ErrorLoadingException("Could not find title")
|
||||||
|
|
||||||
val body =
|
val body =
|
||||||
app.get("$mainUrl/ajax/episode/list/$id?vrf=${encodeVrf(id)}").parsed<Response>().html
|
app.get("$mainUrl/ajax/episode/list/$id?vrf=${encodeVrf(id, cipherKey)}")
|
||||||
|
.parsed<Response>().html
|
||||||
|
|
||||||
val subEpisodes = ArrayList<Episode>()
|
val subEpisodes = ArrayList<Episode>()
|
||||||
val dubEpisodes = ArrayList<Episode>()
|
val dubEpisodes = ArrayList<Episode>()
|
||||||
|
@ -253,7 +256,7 @@ class NineAnimeProvider : MainAPI() {
|
||||||
ids.getOrNull(1)?.let { dub ->
|
ids.getOrNull(1)?.let { dub ->
|
||||||
dubEpisodes.add(
|
dubEpisodes.add(
|
||||||
Episode(
|
Episode(
|
||||||
"$mainUrl/ajax/server/list/$dub?vrf=${encodeVrf(dub)}",
|
"$mainUrl/ajax/server/list/$dub?vrf=${encodeVrf(dub, cipherKey)}",
|
||||||
epTitle,
|
epTitle,
|
||||||
episode = epNum
|
episode = epNum
|
||||||
)
|
)
|
||||||
|
@ -262,7 +265,7 @@ class NineAnimeProvider : MainAPI() {
|
||||||
ids.getOrNull(0)?.let { sub ->
|
ids.getOrNull(0)?.let { sub ->
|
||||||
subEpisodes.add(
|
subEpisodes.add(
|
||||||
Episode(
|
Episode(
|
||||||
"$mainUrl/ajax/server/list/$sub?vrf=${encodeVrf(sub)}",
|
"$mainUrl/ajax/server/list/$sub?vrf=${encodeVrf(sub, cipherKey)}",
|
||||||
epTitle,
|
epTitle,
|
||||||
episode = epNum
|
episode = epNum
|
||||||
)
|
)
|
||||||
|
@ -321,7 +324,7 @@ class NineAnimeProvider : MainAPI() {
|
||||||
|
|
||||||
//TODO 9anime outro into {"status":200,"result":{"url":"","skip_data":{"intro_begin":67,"intro_end":154,"outro_begin":1337,"outro_end":1415,"count":3}},"message":"","messages":[]}
|
//TODO 9anime outro into {"status":200,"result":{"url":"","skip_data":{"intro_begin":67,"intro_end":154,"outro_begin":1337,"outro_end":1415,"count":3}},"message":"","messages":[]}
|
||||||
private suspend fun getEpisodeLinks(id: String): Links? {
|
private suspend fun getEpisodeLinks(id: String): Links? {
|
||||||
return app.get("$mainUrl/ajax/server/$id?vrf=${encodeVrf(id)}").parsedSafe()
|
return app.get("$mainUrl/ajax/server/$id?vrf=${encodeVrf(id, cipherKey)}").parsedSafe()
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun loadLinks(
|
override suspend fun loadLinks(
|
||||||
|
@ -338,7 +341,7 @@ class NineAnimeProvider : MainAPI() {
|
||||||
val name = it.text()
|
val name = it.text()
|
||||||
val encodedStreamUrl =
|
val encodedStreamUrl =
|
||||||
getEpisodeLinks(it.attr("data-link-id"))?.result?.url ?: return@apmap
|
getEpisodeLinks(it.attr("data-link-id"))?.result?.url ?: return@apmap
|
||||||
val url = decodeVrf(encodedStreamUrl)
|
val url = decodeVrf(encodedStreamUrl, cipherKey)
|
||||||
if (!loadExtractor(url, mainUrl, subtitleCallback, callback)) {
|
if (!loadExtractor(url, mainUrl, subtitleCallback, callback)) {
|
||||||
callback(
|
callback(
|
||||||
ExtractorLink(
|
ExtractorLink(
|
||||||
|
|
|
@ -1,82 +1,7 @@
|
||||||
package com.lagradost.cloudstream3.extractors
|
package com.lagradost.cloudstream3.extractors
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty
|
open class Mcloud : WcoStream() {
|
||||||
import com.lagradost.cloudstream3.USER_AGENT
|
|
||||||
import com.lagradost.cloudstream3.apmap
|
|
||||||
import com.lagradost.cloudstream3.app
|
|
||||||
import com.lagradost.cloudstream3.extractors.WcoStream.Companion.cipher
|
|
||||||
import com.lagradost.cloudstream3.extractors.WcoStream.Companion.encrypt
|
|
||||||
import com.lagradost.cloudstream3.extractors.WcoStream.Companion.keytwo
|
|
||||||
import com.lagradost.cloudstream3.extractors.helper.WcoHelper.Companion.getNewWcoKey
|
|
||||||
import com.lagradost.cloudstream3.extractors.helper.WcoHelper.Companion.getWcoKey
|
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
|
|
||||||
import com.lagradost.cloudstream3.utils.ExtractorApi
|
|
||||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
|
||||||
import com.lagradost.cloudstream3.utils.M3u8Helper.Companion.generateM3u8
|
|
||||||
|
|
||||||
open class Mcloud : ExtractorApi() {
|
|
||||||
override var name = "Mcloud"
|
override var name = "Mcloud"
|
||||||
override var mainUrl = "https://mcloud.to"
|
override var mainUrl = "https://mcloud.to"
|
||||||
override val requiresReferer = true
|
override val requiresReferer = true
|
||||||
val headers = mapOf(
|
|
||||||
"Host" to "mcloud.to",
|
|
||||||
"User-Agent" to USER_AGENT,
|
|
||||||
"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",
|
|
||||||
"DNT" to "1",
|
|
||||||
"Connection" to "keep-alive",
|
|
||||||
"Upgrade-Insecure-Requests" to "1",
|
|
||||||
"Sec-Fetch-Dest" to "iframe",
|
|
||||||
"Sec-Fetch-Mode" to "navigate",
|
|
||||||
"Sec-Fetch-Site" to "cross-site",
|
|
||||||
"Referer" to "https://animekisa.in/", //Referer works for wco and animekisa, probably with others too
|
|
||||||
"Pragma" to "no-cache",
|
|
||||||
"Cache-Control" to "no-cache",)
|
|
||||||
private val key = "LCbu3iYC7ln24K7P" // key credits @Modder4869
|
|
||||||
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink>? {
|
|
||||||
val id = url.substringAfter("e/").substringAfter("embed/").substringBefore("?")
|
|
||||||
val keys = getNewWcoKey()
|
|
||||||
keytwo = keys?.encryptKey ?: return null
|
|
||||||
val encryptedid = encrypt(cipher(keys.cipherkey!!, encrypt(id))).replace("/", "_").replace("=","")
|
|
||||||
val link = "$mainUrl/mediainfo/$encryptedid?key=${keys.mainKey}"
|
|
||||||
val response = app.get(link, referer = "https://animekisa.in/").text
|
|
||||||
if(response.startsWith("<!DOCTYPE html>")) {
|
|
||||||
// TODO decrypt html for link
|
|
||||||
return emptyList()
|
|
||||||
}
|
|
||||||
data class SourcesMcloud (
|
|
||||||
@JsonProperty("file" ) val file : String
|
|
||||||
)
|
|
||||||
|
|
||||||
data class MediaMcloud (
|
|
||||||
@JsonProperty("sources" ) val sources : ArrayList<SourcesMcloud> = arrayListOf()
|
|
||||||
)
|
|
||||||
|
|
||||||
data class DataMcloud (
|
|
||||||
@JsonProperty("media" ) val media : MediaMcloud? = MediaMcloud()
|
|
||||||
)
|
|
||||||
|
|
||||||
data class JsonMcloud (
|
|
||||||
@JsonProperty("status" ) val status : Int? = null,
|
|
||||||
@JsonProperty("data" ) val data : DataMcloud = DataMcloud()
|
|
||||||
)
|
|
||||||
|
|
||||||
val mapped = parseJson<JsonMcloud>(response)
|
|
||||||
val sources = mutableListOf<ExtractorLink>()
|
|
||||||
val checkfile = mapped.status == 200
|
|
||||||
if (checkfile)
|
|
||||||
mapped.data.media?.sources?.apmap {
|
|
||||||
if (it.file.contains("m3u8")) {
|
|
||||||
sources.addAll(
|
|
||||||
generateM3u8(
|
|
||||||
name,
|
|
||||||
it.file,
|
|
||||||
url,
|
|
||||||
headers = mapOf("Referer" to url)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return sources
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -1,15 +1,13 @@
|
||||||
package com.lagradost.cloudstream3.extractors
|
package com.lagradost.cloudstream3.extractors
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty
|
import com.fasterxml.jackson.annotation.JsonProperty
|
||||||
import com.lagradost.cloudstream3.apmap
|
import com.lagradost.cloudstream3.ErrorLoadingException
|
||||||
|
import com.lagradost.cloudstream3.animeproviders.NineAnimeProvider.Companion.cipher
|
||||||
|
import com.lagradost.cloudstream3.animeproviders.NineAnimeProvider.Companion.encrypt
|
||||||
import com.lagradost.cloudstream3.app
|
import com.lagradost.cloudstream3.app
|
||||||
import com.lagradost.cloudstream3.extractors.helper.WcoHelper.Companion.getNewWcoKey
|
|
||||||
import com.lagradost.cloudstream3.extractors.helper.WcoHelper.Companion.getWcoKey
|
|
||||||
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.Companion.generateM3u8
|
|
||||||
import com.lagradost.cloudstream3.utils.Qualities
|
import com.lagradost.cloudstream3.utils.Qualities
|
||||||
import com.lagradost.cloudstream3.utils.getQualityFromName
|
|
||||||
|
|
||||||
class Vidstreamz : WcoStream() {
|
class Vidstreamz : WcoStream() {
|
||||||
override var mainUrl = "https://vidstreamz.online"
|
override var mainUrl = "https://vidstreamz.online"
|
||||||
|
@ -51,173 +49,79 @@ class VizcloudCloud : WcoStream() {
|
||||||
override var mainUrl = "https://vizcloud.cloud"
|
override var mainUrl = "https://vizcloud.cloud"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class VizcloudSite : WcoStream() {
|
||||||
|
override var mainUrl = "https://vizcloud.site"
|
||||||
|
}
|
||||||
|
|
||||||
open class WcoStream : ExtractorApi() {
|
open class WcoStream : ExtractorApi() {
|
||||||
override var name = "VidStream" // Cause works for animekisa and wco
|
override var name = "VidStream" // Cause works for animekisa and wco
|
||||||
override var mainUrl = "https://vidstream.pro"
|
override var mainUrl = "https://vidstream.pro"
|
||||||
override val requiresReferer = false
|
override val requiresReferer = false
|
||||||
|
private val regex = Regex("(.+?/)e(?:mbed)?/([a-zA-Z0-9]+)")
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
var keytwo = ""
|
// taken from https://github.com/saikou-app/saikou/blob/b35364c8c2a00364178a472fccf1ab72f09815b4/app/src/main/java/ani/saikou/parsers/anime/extractors/VizCloud.kt
|
||||||
fun encrypt(input: String): String {
|
// GNU General Public License v3.0 https://github.com/saikou-app/saikou/blob/main/LICENSE.md
|
||||||
if (input.any { it.code >= 256 }) throw Exception("illegal characters!")
|
private var lastChecked = 0L
|
||||||
var output = ""
|
private const val jsonLink =
|
||||||
for (i in input.indices step 3) {
|
"https://raw.githubusercontent.com/chenkaslowankiya/BruhFlow/main/keys.json"
|
||||||
val a = intArrayOf(-1, -1, -1, -1)
|
private var cipherKey: VizCloudKey? = null
|
||||||
a[0] = input[i].code shr 2
|
suspend fun getKey(): VizCloudKey {
|
||||||
a[1] = (3 and input[i].code) shl 4
|
cipherKey =
|
||||||
if (input.length > i + 1) {
|
if (cipherKey != null && (lastChecked - System.currentTimeMillis()) < 1000 * 60 * 30) cipherKey!!
|
||||||
a[1] = a[1] or (input[i + 1].code shr 4)
|
else {
|
||||||
a[2] = (15 and input[i + 1].code) shl 2
|
lastChecked = System.currentTimeMillis()
|
||||||
|
app.get(jsonLink).parsed()
|
||||||
}
|
}
|
||||||
if (input.length > i + 2) {
|
return cipherKey!!
|
||||||
a[2] = a[2] or (input[i + 2].code shr 6)
|
|
||||||
a[3] = 63 and input[i + 2].code
|
|
||||||
}
|
|
||||||
for (n in a) {
|
|
||||||
if (n == -1) output += "="
|
|
||||||
else {
|
|
||||||
if (n in 0..63) output += keytwo[n]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return output;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun cipher(inputOne: String, inputTwo: String): String {
|
data class VizCloudKey(
|
||||||
val arr = IntArray(256) { it }
|
@JsonProperty("cipherKey") val cipherKey: String,
|
||||||
var output = ""
|
@JsonProperty("mainKey") val mainKey: String,
|
||||||
var u = 0
|
@JsonProperty("encryptKey") val encryptKey: String,
|
||||||
var r: Int
|
@JsonProperty("dashTable") val dashTable: String
|
||||||
for (a in arr.indices) {
|
)
|
||||||
u = (u + arr[a] + inputOne[a % inputOne.length].code) % 256
|
|
||||||
r = arr[a]
|
private const val baseTable =
|
||||||
arr[a] = arr[u]
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+=/_"
|
||||||
arr[u] = r
|
|
||||||
}
|
private fun dashify(id: String, dashTable: String): String {
|
||||||
u = 0
|
val table = dashTable.split(" ")
|
||||||
var c = 0
|
return id.mapIndexedNotNull { i, c ->
|
||||||
for (f in inputTwo.indices) {
|
table.getOrNull((baseTable.indexOf(c) * 16) + (i % 16))
|
||||||
c = (c + f) % 256
|
}.joinToString("-")
|
||||||
u = (u + arr[c]) % 256
|
|
||||||
r = arr[c]
|
|
||||||
arr[c] = arr[u]
|
|
||||||
arr[u] = r
|
|
||||||
output += (inputTwo[f].code xor arr[(arr[c] + arr[u]) % 256]).toChar()
|
|
||||||
}
|
|
||||||
return output
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private val key = "LCbu3iYC7ln24K7P" // key credits @Modder4869
|
|
||||||
|
//private val key = "LCbu3iYC7ln24K7P" // key credits @Modder4869
|
||||||
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink> {
|
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink> {
|
||||||
val baseUrl = url.split("/e/")[0]
|
val group = regex.find(url)?.groupValues!!
|
||||||
|
|
||||||
val (Id) = (Regex("/e/(.*?)?domain").find(url)?.destructured ?: Regex("""/e/(.*)""").find(
|
val host = group[1]
|
||||||
url
|
val viz = getKey()
|
||||||
)?.destructured) ?: return emptyList()
|
val id = encrypt(
|
||||||
// val (skey) = Regex("""skey\s=\s['"](.*?)['"];""").find(html)?.destructured
|
cipher(
|
||||||
// ?: return emptyList()
|
viz.cipherKey,
|
||||||
val keys = getNewWcoKey()
|
encrypt(group[2], viz.encryptKey).also { println(it) }
|
||||||
keytwo = keys?.encryptKey ?: return emptyList()
|
).also { println(it) },
|
||||||
val encryptedID = encrypt(cipher(keys.cipherkey!!, encrypt(Id))).replace("/", "_").replace("=","")
|
viz.encryptKey
|
||||||
val apiLink = "$baseUrl/mediainfo/$encryptedID?key=${keys.mainKey}"
|
).also { println(it) }
|
||||||
val referrer = "$baseUrl/e/$Id?domain=wcostream.cc"
|
|
||||||
|
|
||||||
data class SourcesWco (
|
val link =
|
||||||
@JsonProperty("file" ) val file : String
|
"${host}mediainfo/${dashify(id, viz.dashTable)}?key=${viz.mainKey}" //
|
||||||
)
|
val response = app.get(link, referer = referer)
|
||||||
|
|
||||||
data class MediaWco (
|
data class Sources(@JsonProperty("file") val file: String)
|
||||||
@JsonProperty("sources" ) val sources : ArrayList<SourcesWco> = arrayListOf()
|
data class Media(@JsonProperty("sources") val sources: List<Sources>)
|
||||||
)
|
data class Data(@JsonProperty("media") val media: Media)
|
||||||
|
data class Response(@JsonProperty("data") val data: Data)
|
||||||
|
|
||||||
data class DataWco (
|
|
||||||
@JsonProperty("media" ) val media : MediaWco? = MediaWco()
|
|
||||||
)
|
|
||||||
|
|
||||||
data class WcoResponse (
|
if (!response.text.startsWith("{")) throw ErrorLoadingException("Seems like 9Anime kiddies changed stuff again, Go touch some grass for bout an hour Or use a different Server")
|
||||||
@JsonProperty("status" ) val status : Int? = null,
|
return response.parsed<Response>().data.media.sources.map {
|
||||||
@JsonProperty("data" ) val data : DataWco? = DataWco()
|
ExtractorLink(name, it.file,it.file,host,Qualities.Unknown.value,it.file.contains(".m3u8"))
|
||||||
)
|
|
||||||
|
|
||||||
val mapped = app.get(apiLink, headers = mapOf("Referer" to referrer)).parsed<WcoResponse>()
|
|
||||||
val sources = mutableListOf<ExtractorLink>()
|
|
||||||
val check = mapped.status == 200
|
|
||||||
if (check) {
|
|
||||||
mapped.data?.media?.sources?.forEach {
|
|
||||||
if (mainUrl == "https://vizcloud2.ru" || mainUrl == "https://vizcloud.online") {
|
|
||||||
if (it.file.contains("vizcloud2.ru") || it.file.contains("vizcloud.online")) {
|
|
||||||
// Had to do this thing 'cause "list.m3u8#.mp4" gives 404 error so no quality is added
|
|
||||||
val link1080 = it.file.replace("list.m3u8#.mp4", "H4/v.m3u8")
|
|
||||||
val link720 = it.file.replace("list.m3u8#.mp4", "H3/v.m3u8")
|
|
||||||
val link480 = it.file.replace("list.m3u8#.mp4", "H2/v.m3u8")
|
|
||||||
val link360 = it.file.replace("list.m3u8#.mp4", "H1/v.m3u8")
|
|
||||||
val linkauto = it.file.replace("#.mp4", "")
|
|
||||||
listOf(
|
|
||||||
link1080,
|
|
||||||
link720,
|
|
||||||
link480,
|
|
||||||
link360,
|
|
||||||
linkauto
|
|
||||||
).apmap { serverurl ->
|
|
||||||
val testurl = app.get(serverurl, headers = mapOf("Referer" to url)).text
|
|
||||||
if (testurl.contains("EXTM3")) {
|
|
||||||
val quality = when {
|
|
||||||
serverurl.contains("H4") -> "1080p"
|
|
||||||
serverurl.contains("H3") -> "720p"
|
|
||||||
serverurl.contains("H2") -> "480p"
|
|
||||||
serverurl.contains("H1") -> "360p"
|
|
||||||
else -> "Auto"
|
|
||||||
}
|
|
||||||
sources.add(
|
|
||||||
ExtractorLink(
|
|
||||||
"VidStream",
|
|
||||||
"VidStream",
|
|
||||||
serverurl,
|
|
||||||
url,
|
|
||||||
getQualityFromName(quality),
|
|
||||||
true,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (
|
|
||||||
arrayOf(
|
|
||||||
"https://vidstream.pro",
|
|
||||||
"https://vidstreamz.online",
|
|
||||||
"https://vizcloud2.online",
|
|
||||||
"https://vizcloud.xyz",
|
|
||||||
"https://vizcloud.live",
|
|
||||||
"https://vizcloud.info",
|
|
||||||
"https://mwvn.vizcloud.info",
|
|
||||||
"https://vizcloud.digital",
|
|
||||||
"https://vizcloud.cloud"
|
|
||||||
).contains(mainUrl)
|
|
||||||
) {
|
|
||||||
if (it.file.contains("m3u8")) {
|
|
||||||
sources.addAll(
|
|
||||||
generateM3u8(
|
|
||||||
name,
|
|
||||||
it.file.replace("#.mp4", ""),
|
|
||||||
url,
|
|
||||||
headers = mapOf("Referer" to url)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
sources.add(
|
|
||||||
ExtractorLink(
|
|
||||||
name,
|
|
||||||
name = name,
|
|
||||||
it.file,
|
|
||||||
"",
|
|
||||||
Qualities.P720.value,
|
|
||||||
false
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return sources
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,9 @@ package com.lagradost.cloudstream3.movieproviders
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty
|
import com.fasterxml.jackson.annotation.JsonProperty
|
||||||
import com.lagradost.cloudstream3.*
|
import com.lagradost.cloudstream3.*
|
||||||
|
import com.lagradost.cloudstream3.animeproviders.NineAnimeProvider.Companion.decodeVrf
|
||||||
|
import com.lagradost.cloudstream3.animeproviders.NineAnimeProvider.Companion.encode
|
||||||
|
import com.lagradost.cloudstream3.animeproviders.NineAnimeProvider.Companion.encodeVrf
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
|
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
|
||||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||||
import com.lagradost.cloudstream3.utils.loadExtractor
|
import com.lagradost.cloudstream3.utils.loadExtractor
|
||||||
|
@ -62,113 +65,8 @@ open class BflixProvider : MainAPI() {
|
||||||
return HomePageResponse(items)
|
return HomePageResponse(items)
|
||||||
}
|
}
|
||||||
|
|
||||||
//Credits to https://github.com/jmir1
|
|
||||||
private val key =
|
|
||||||
"5uLKesbh0nkrpPq9VwMC6+tQBdomjJ4HNl/fWOSiREvAYagT8yIG7zx2D13UZFXc" //key credits to @Modder4869
|
|
||||||
|
|
||||||
private fun getVrf(id: String): String? {
|
|
||||||
val reversed = ue(encode(id) + "0000000").slice(0..5).reversed()
|
|
||||||
return reversed + ue(je(reversed, encode(id)?.replace("+", "%20") ?: return null)).replace(
|
|
||||||
"""=+$""".toRegex(),
|
|
||||||
""
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getLink(url: String): String? {
|
|
||||||
val i = url.slice(0..5)
|
|
||||||
val n = url.slice(6..url.lastIndex)
|
|
||||||
return decode(je(i, ze(n)))
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun ue(input: String): String {
|
|
||||||
if (input.any { it.code >= 256 }) throw Exception("illegal characters!")
|
|
||||||
var output = ""
|
|
||||||
for (i in input.indices step 3) {
|
|
||||||
val a = intArrayOf(-1, -1, -1, -1)
|
|
||||||
a[0] = input[i].code shr 2
|
|
||||||
a[1] = (3 and input[i].code) shl 4
|
|
||||||
if (input.length > i + 1) {
|
|
||||||
a[1] = a[1] or (input[i + 1].code shr 4)
|
|
||||||
a[2] = (15 and input[i + 1].code) shl 2
|
|
||||||
}
|
|
||||||
if (input.length > i + 2) {
|
|
||||||
a[2] = a[2] or (input[i + 2].code shr 6)
|
|
||||||
a[3] = 63 and input[i + 2].code
|
|
||||||
}
|
|
||||||
for (n in a) {
|
|
||||||
if (n == -1) output += "="
|
|
||||||
else {
|
|
||||||
if (n in 0..63) output += key[n]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return output
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun je(inputOne: String, inputTwo: String): String {
|
|
||||||
val arr = IntArray(256) { it }
|
|
||||||
var output = ""
|
|
||||||
var u = 0
|
|
||||||
var r: Int
|
|
||||||
for (a in arr.indices) {
|
|
||||||
u = (u + arr[a] + inputOne[a % inputOne.length].code) % 256
|
|
||||||
r = arr[a]
|
|
||||||
arr[a] = arr[u]
|
|
||||||
arr[u] = r
|
|
||||||
}
|
|
||||||
u = 0
|
|
||||||
var c = 0
|
|
||||||
for (f in inputTwo.indices) {
|
|
||||||
c = (c + f) % 256
|
|
||||||
u = (u + arr[c]) % 256
|
|
||||||
r = arr[c]
|
|
||||||
arr[c] = arr[u]
|
|
||||||
arr[u] = r
|
|
||||||
output += (inputTwo[f].code xor arr[(arr[c] + arr[u]) % 256]).toChar()
|
|
||||||
}
|
|
||||||
return output
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun ze(input: String): String {
|
|
||||||
val t = if (input.replace("""[\t\n\f\r]""".toRegex(), "").length % 4 == 0) {
|
|
||||||
input.replace("""/==?$/""".toRegex(), "")
|
|
||||||
} else input
|
|
||||||
if (t.length % 4 == 1 || t.contains("""[^+/0-9A-Za-z]""".toRegex())) throw Exception("bad input")
|
|
||||||
var i: Int
|
|
||||||
var r = ""
|
|
||||||
var e = 0
|
|
||||||
var u = 0
|
|
||||||
for (o in t.indices) {
|
|
||||||
e = e shl 6
|
|
||||||
i = key.indexOf(t[o])
|
|
||||||
e = e or i
|
|
||||||
u += 6
|
|
||||||
if (24 == u) {
|
|
||||||
r += ((16711680 and e) shr 16).toChar()
|
|
||||||
r += ((65280 and e) shr 8).toChar()
|
|
||||||
r += (255 and e).toChar()
|
|
||||||
e = 0
|
|
||||||
u = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return if (12 == u) {
|
|
||||||
e = e shr 4
|
|
||||||
r + e.toChar()
|
|
||||||
} else {
|
|
||||||
if (18 == u) {
|
|
||||||
e = e shr 2
|
|
||||||
r += ((65280 and e) shr 8).toChar()
|
|
||||||
r += (255 and e).toChar()
|
|
||||||
}
|
|
||||||
r
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun encode(input: String): String? = java.net.URLEncoder.encode(input, "utf-8")
|
|
||||||
private fun decode(input: String): String? = java.net.URLDecoder.decode(input, "utf-8")
|
|
||||||
|
|
||||||
override suspend fun search(query: String): List<SearchResponse>? {
|
override suspend fun search(query: String): List<SearchResponse>? {
|
||||||
val encodedquery = getVrf(query)?.let { encode(it) } ?: return null
|
val encodedquery = encodeVrf(query, mainKey)
|
||||||
val url = "$mainUrl/search?keyword=$query&vrf=$encodedquery"
|
val url = "$mainUrl/search?keyword=$query&vrf=$encodedquery"
|
||||||
val html = app.get(url).text
|
val html = app.get(url).text
|
||||||
val document = Jsoup.parse(html)
|
val document = Jsoup.parse(html)
|
||||||
|
@ -210,10 +108,14 @@ open class BflixProvider : MainAPI() {
|
||||||
@JsonProperty("html") val html: String
|
@JsonProperty("html") val html: String
|
||||||
)
|
)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val mainKey = "OrAimkpzm6phmN3j"
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun load(url: String): LoadResponse? {
|
override suspend fun load(url: String): LoadResponse? {
|
||||||
val soup = app.get(url).document
|
val soup = app.get(url).document
|
||||||
val movieid = soup.selectFirst("div#watch")!!.attr("data-id")
|
val movieid = soup.selectFirst("div#watch")!!.attr("data-id")
|
||||||
val movieidencoded = encode(getVrf(movieid) ?: return null)
|
val movieidencoded = encodeVrf(movieid, mainKey)
|
||||||
val title = soup.selectFirst("div.info h1")!!.text()
|
val title = soup.selectFirst("div.info h1")!!.text()
|
||||||
val description = soup.selectFirst(".info .desc")?.text()?.trim()
|
val description = soup.selectFirst(".info .desc")?.text()?.trim()
|
||||||
val poster: String? = try {
|
val poster: String? = try {
|
||||||
|
@ -223,9 +125,11 @@ open class BflixProvider : MainAPI() {
|
||||||
}
|
}
|
||||||
|
|
||||||
val tags = soup.select("div.info .meta div:contains(Genre) a").map { it.text() }
|
val tags = soup.select("div.info .meta div:contains(Genre) a").map { it.text() }
|
||||||
|
val vrfUrl = "$mainUrl/ajax/film/servers?id=$movieid&vrf=$movieidencoded"
|
||||||
|
println("VRF___ $vrfUrl")
|
||||||
val episodes = Jsoup.parse(
|
val episodes = Jsoup.parse(
|
||||||
app.get(
|
app.get(
|
||||||
"$mainUrl/ajax/film/servers?id=$movieid&vrf=$movieidencoded"
|
vrfUrl
|
||||||
).parsed<Response>().html
|
).parsed<Response>().html
|
||||||
).select("div.episode").map {
|
).select("div.episode").map {
|
||||||
val a = it.selectFirst("a")
|
val a = it.selectFirst("a")
|
||||||
|
@ -251,7 +155,7 @@ open class BflixProvider : MainAPI() {
|
||||||
if (url.contains("/movie/") && episodes.size == 1) TvType.Movie else TvType.TvSeries
|
if (url.contains("/movie/") && episodes.size == 1) TvType.Movie else TvType.TvSeries
|
||||||
val recommendations =
|
val recommendations =
|
||||||
soup.select("div.bl-2 section.bl div.content div.filmlist div.item")
|
soup.select("div.bl-2 section.bl div.content div.filmlist div.item")
|
||||||
?.mapNotNull { element ->
|
.mapNotNull { element ->
|
||||||
val recTitle = element.select("h3 a").text() ?: return@mapNotNull null
|
val recTitle = element.select("h3 a").text() ?: return@mapNotNull null
|
||||||
val image = element.select("a.poster img")?.attr("src")
|
val image = element.select("a.poster img")?.attr("src")
|
||||||
val recUrl = fixUrl(element.select("a").attr("href"))
|
val recUrl = fixUrl(element.select("a").attr("href"))
|
||||||
|
@ -267,13 +171,13 @@ open class BflixProvider : MainAPI() {
|
||||||
val rating = soup.selectFirst(".info span.imdb")?.text()?.toRatingInt()
|
val rating = soup.selectFirst(".info span.imdb")?.text()?.toRatingInt()
|
||||||
val durationdoc = soup.selectFirst("div.info div.meta").toString()
|
val durationdoc = soup.selectFirst("div.info div.meta").toString()
|
||||||
val durationregex = Regex("((\\d+) min)")
|
val durationregex = Regex("((\\d+) min)")
|
||||||
val yearegex = Regex("<span>(\\d+)<\\/span>")
|
val yearegex = Regex("<span>(\\d+)</span>")
|
||||||
val duration = if (durationdoc.contains("na min")) null
|
val duration = if (durationdoc.contains("na min")) null
|
||||||
else durationregex.find(durationdoc)?.destructured?.component1()?.replace(" min", "")
|
else durationregex.find(durationdoc)?.destructured?.component1()?.replace(" min", "")
|
||||||
?.toIntOrNull()
|
?.toIntOrNull()
|
||||||
val year = if (mainUrl == "https://bflix.ru") {
|
val year = if (mainUrl == "https://bflix.ru") {
|
||||||
yearegex.find(durationdoc)?.destructured?.component1()
|
yearegex.find(durationdoc)?.destructured?.component1()
|
||||||
?.replace(Regex("<span>|<\\/span>"), "")
|
?.replace(Regex("<span>|</span>"), "")
|
||||||
} else null
|
} else null
|
||||||
return when (tvType) {
|
return when (tvType) {
|
||||||
TvType.TvSeries -> {
|
TvType.TvSeries -> {
|
||||||
|
@ -341,7 +245,7 @@ open class BflixProvider : MainAPI() {
|
||||||
val soup = app.get(data).document
|
val soup = app.get(data).document
|
||||||
|
|
||||||
val movieid = encode(soup.selectFirst("div#watch")?.attr("data-id") ?: return false)
|
val movieid = encode(soup.selectFirst("div#watch")?.attr("data-id") ?: return false)
|
||||||
val movieidencoded = encode(getVrf(movieid!!) ?: return false)
|
val movieidencoded = encodeVrf(movieid, mainKey)
|
||||||
Jsoup.parse(
|
Jsoup.parse(
|
||||||
parseJson<Response>(
|
parseJson<Response>(
|
||||||
app.get(
|
app.get(
|
||||||
|
@ -372,8 +276,9 @@ open class BflixProvider : MainAPI() {
|
||||||
val epserver = app.get("$mainUrl/ajax/episode/info?id=$it").text
|
val epserver = app.get("$mainUrl/ajax/episode/info?id=$it").text
|
||||||
(if (epserver.contains("url")) {
|
(if (epserver.contains("url")) {
|
||||||
parseJson<Links>(epserver)
|
parseJson<Links>(epserver)
|
||||||
} else null)?.url?.let { it1 -> getLink(it1.replace("=", "")) }
|
} else null)?.url?.let {
|
||||||
?.replace("/embed/", "/e/")?.replace(Regex("(\\?sub.info.*)"), "")
|
decodeVrf(it, mainKey)
|
||||||
|
}
|
||||||
}.apmap { url ->
|
}.apmap { url ->
|
||||||
loadExtractor(
|
loadExtractor(
|
||||||
url, data, subtitleCallback, callback
|
url, data, subtitleCallback, callback
|
||||||
|
|
|
@ -22,7 +22,7 @@ import com.google.android.exoplayer2.upstream.cache.LeastRecentlyUsedCacheEvicto
|
||||||
import com.google.android.exoplayer2.upstream.cache.SimpleCache
|
import com.google.android.exoplayer2.upstream.cache.SimpleCache
|
||||||
import com.google.android.exoplayer2.util.MimeTypes
|
import com.google.android.exoplayer2.util.MimeTypes
|
||||||
import com.google.android.exoplayer2.video.VideoSize
|
import com.google.android.exoplayer2.video.VideoSize
|
||||||
import com.lagradost.cloudstream3.APIHolder.getApiFromName
|
import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull
|
||||||
import com.lagradost.cloudstream3.USER_AGENT
|
import com.lagradost.cloudstream3.USER_AGENT
|
||||||
import com.lagradost.cloudstream3.app
|
import com.lagradost.cloudstream3.app
|
||||||
import com.lagradost.cloudstream3.mvvm.logError
|
import com.lagradost.cloudstream3.mvvm.logError
|
||||||
|
@ -210,6 +210,7 @@ class CS3IPlayer : IPlayer {
|
||||||
}
|
}
|
||||||
|
|
||||||
var currentSubtitles: SubtitleData? = null
|
var currentSubtitles: SubtitleData? = null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return True if the player should be reloaded
|
* @return True if the player should be reloaded
|
||||||
* */
|
* */
|
||||||
|
@ -353,8 +354,8 @@ class CS3IPlayer : IPlayer {
|
||||||
var requestSubtitleUpdate: (() -> Unit)? = null
|
var requestSubtitleUpdate: (() -> Unit)? = null
|
||||||
|
|
||||||
private fun createOnlineSource(link: ExtractorLink): DataSource.Factory {
|
private fun createOnlineSource(link: ExtractorLink): DataSource.Factory {
|
||||||
val provider = getApiFromName(link.source)
|
val provider = getApiFromNameNull(link.source)
|
||||||
val interceptor = provider.getVideoInterceptor(link)
|
val interceptor = provider?.getVideoInterceptor(link)
|
||||||
|
|
||||||
val source = if (interceptor == null) {
|
val source = if (interceptor == null) {
|
||||||
DefaultHttpDataSource.Factory() //TODO USE app.baseClient
|
DefaultHttpDataSource.Factory() //TODO USE app.baseClient
|
||||||
|
|
|
@ -149,18 +149,14 @@ class SettingsLang : PreferenceFragmentCompat() {
|
||||||
|
|
||||||
getPref(R.string.provider_lang_key)?.setOnPreferenceClickListener {
|
getPref(R.string.provider_lang_key)?.setOnPreferenceClickListener {
|
||||||
activity?.getApiProviderLangSettings()?.let { current ->
|
activity?.getApiProviderLangSettings()?.let { current ->
|
||||||
var allLangs: MutableSet<String> = mutableSetOf()
|
val langs = APIHolder.apis.map { it.lang }.toSet().sortedBy { SubtitleHelper.fromTwoLettersToLanguage(it) }
|
||||||
for (api in APIHolder.apis) {
|
|
||||||
allLangs.add(api.lang)
|
|
||||||
}
|
|
||||||
allLangs = allLangs.sortedBy { SubtitleHelper.fromTwoLettersToLanguage(it) }.toMutableSet()
|
|
||||||
|
|
||||||
val currentList = ArrayList<Int>()
|
val currentList = ArrayList<Int>()
|
||||||
for (i in current) {
|
for (i in current) {
|
||||||
currentList.add(allLangs.indexOf(i))
|
currentList.add(langs.indexOf(i))
|
||||||
}
|
}
|
||||||
|
|
||||||
val names = allLangs.map {
|
val names = langs.map {
|
||||||
val emoji = SubtitleHelper.getFlagFromIso(it)
|
val emoji = SubtitleHelper.getFlagFromIso(it)
|
||||||
val name = SubtitleHelper.fromTwoLettersToLanguage(it)
|
val name = SubtitleHelper.fromTwoLettersToLanguage(it)
|
||||||
val fullName = "$emoji $name"
|
val fullName = "$emoji $name"
|
||||||
|
|
|
@ -202,6 +202,7 @@ val extractorApis: Array<ExtractorApi> = arrayOf(
|
||||||
MwvnVizcloudInfo(),
|
MwvnVizcloudInfo(),
|
||||||
VizcloudDigital(),
|
VizcloudDigital(),
|
||||||
VizcloudCloud(),
|
VizcloudCloud(),
|
||||||
|
VizcloudSite(),
|
||||||
VideoVard(),
|
VideoVard(),
|
||||||
VideovardSX(),
|
VideovardSX(),
|
||||||
Mp4Upload(),
|
Mp4Upload(),
|
||||||
|
|
Loading…
Reference in a new issue