From 5103ad09dc998d1cbfed197e83b5de594f8375ec Mon Sep 17 00:00:00 2001
From: LagradOst <11805592+LagradOst@users.noreply.github.com>
Date: Fri, 4 Aug 2023 17:20:23 +0200
Subject: [PATCH 1/3] reverted gradle bump
---
app/build.gradle.kts | 6 +++---
build.gradle.kts | 3 ++-
gradle/wrapper/gradle-wrapper.properties | 2 +-
3 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 5c864117..9300775c 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -115,11 +115,11 @@ android {
compileOptions {
isCoreLibraryDesugaringEnabled = true
- sourceCompatibility = JavaVersion.VERSION_17
- targetCompatibility = JavaVersion.VERSION_17
+ sourceCompatibility = JavaVersion.VERSION_1_8
+ targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
- jvmTarget = "17"
+ jvmTarget = "1.8"
freeCompilerArgs = listOf("-Xjvm-default=compatibility")
}
lint {
diff --git a/build.gradle.kts b/build.gradle.kts
index 972a4caf..762e4588 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -5,7 +5,8 @@ buildscript {
mavenCentral()
}
dependencies {
- classpath("com.android.tools.build:gradle:8.0.2")
+ // we stay on low ver because prerelease build gradle is fucked
+ classpath("com.android.tools.build:gradle:7.3.1")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.20")
classpath("org.jetbrains.dokka:dokka-gradle-plugin:1.5.0")
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index d4745142..baa28c97 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
#Fri Apr 30 17:11:15 CEST 2021
distributionBase=GRADLE_USER_HOME
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
From ca6700e28de32e99080d9b975fac83303bb5a0cc Mon Sep 17 00:00:00 2001
From: self-similarity <137652432+self-similarity@users.noreply.github.com>
Date: Fri, 4 Aug 2023 15:21:20 +0000
Subject: [PATCH 2/3] More meaningful errors when adding repositories (#537)
* More meaningful errors when adding repositories
---
.../settings/extensions/ExtensionsFragment.kt | 21 +++++++++++++++++--
.../settings/extensions/PluginsViewModel.kt | 12 +++++++----
app/src/main/res/values/strings.xml | 2 ++
3 files changed, 29 insertions(+), 6 deletions(-)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/ExtensionsFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/ExtensionsFragment.kt
index 7b72fc3b..8bc947c5 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/ExtensionsFragment.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/ExtensionsFragment.kt
@@ -224,14 +224,31 @@ class ExtensionsFragment : Fragment() {
showToast(R.string.error_invalid_data, Toast.LENGTH_SHORT)
}
} else {
+ val repository = RepositoryManager.parseRepository(url)
+
+ // Exit if wrong repository
+ if (repository == null) {
+ showToast(R.string.no_repository_found_error, Toast.LENGTH_LONG)
+ return@ioSafe
+ }
+
val fixedName = if (!name.isNullOrBlank()) name
- else RepositoryManager.parseRepository(url)?.name ?: "No name"
+ else repository.name
val newRepo = RepositoryData(fixedName, url)
RepositoryManager.addRepository(newRepo)
extensionViewModel.loadStats()
extensionViewModel.loadRepositories()
- this@ExtensionsFragment.activity?.downloadAllPluginsDialog(url, fixedName)
+
+ val plugins = RepositoryManager.getRepoPlugins(url)
+ if (plugins.isNullOrEmpty()) {
+ showToast(R.string.no_plugins_found_error, Toast.LENGTH_LONG)
+ } else {
+ this@ExtensionsFragment.activity?.downloadAllPluginsDialog(
+ url,
+ fixedName
+ )
+ }
}
}
dialog.dismissSafe(activity)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsViewModel.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsViewModel.kt
index 6c68ac17..471105be 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsViewModel.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsViewModel.kt
@@ -86,13 +86,17 @@ class PluginsViewModel : ViewModel() {
}.also { list ->
main {
showToast(
- if (list.isEmpty()) {
- txt(
+ when {
+ // No plugins at all
+ plugins.isEmpty() -> txt(
+ R.string.no_plugins_found_error,
+ )
+ // All plugins downloaded
+ list.isEmpty() -> txt(
R.string.batch_download_nothing_to_download_format,
txt(R.string.plugin)
)
- } else {
- txt(
+ else -> txt(
R.string.batch_download_start_format,
list.size,
txt(if (list.size == 1) R.string.plugin_singular else R.string.plugin)
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 4dd6eadc..3df4b8c1 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -572,6 +572,8 @@
Started downloading %d %s…
Downloaded %d %s
All %s already downloaded
+ No plugins found in repository
+ Repository not found, check the URL and try VPN
Batch download
plugin
plugins
From bbbb7c4982d6f83f236883e2a9ed40d7a2b8eb61 Mon Sep 17 00:00:00 2001
From: Sofie <117321707+Sofie99@users.noreply.github.com>
Date: Sat, 5 Aug 2023 08:11:46 +0700
Subject: [PATCH 3/3] Extractor: added Rabbitstream (#536)
* Extractor: added Rabbitstream
* fix all request
---------
Co-authored-by: Sofie99
---
.../cloudstream3/extractors/Rabbitstream.kt | 170 ++++++++++++++++++
.../cloudstream3/utils/ExtractorApi.kt | 5 +-
2 files changed, 174 insertions(+), 1 deletion(-)
create mode 100644 app/src/main/java/com/lagradost/cloudstream3/extractors/Rabbitstream.kt
diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/Rabbitstream.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/Rabbitstream.kt
new file mode 100644
index 00000000..b686f7d8
--- /dev/null
+++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/Rabbitstream.kt
@@ -0,0 +1,170 @@
+package com.lagradost.cloudstream3.extractors
+
+import com.fasterxml.jackson.annotation.JsonProperty
+import com.lagradost.cloudstream3.ErrorLoadingException
+import com.lagradost.cloudstream3.SubtitleFile
+import com.lagradost.cloudstream3.app
+import com.lagradost.cloudstream3.base64DecodeArray
+import com.lagradost.cloudstream3.utils.AppUtils
+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
+import java.nio.charset.StandardCharsets
+import java.security.MessageDigest
+import javax.crypto.Cipher
+import javax.crypto.spec.IvParameterSpec
+import javax.crypto.spec.SecretKeySpec
+
+// No License found in https://github.com/enimax-anime/key
+// special credits to @enimax for providing key
+class Megacloud : Rabbitstream() {
+ override val name = "Megacloud"
+ override val mainUrl = "https://megacloud.tv"
+ override val embed = "embed-2/ajax/e-1"
+ override val key = "https://raw.githubusercontent.com/enimax-anime/key/e6/key.txt"
+}
+
+class Dokicloud : Rabbitstream() {
+ override val name = "Dokicloud"
+ override val mainUrl = "https://dokicloud.one"
+}
+
+open class Rabbitstream : ExtractorApi() {
+ override val name = "Rabbitstream"
+ override val mainUrl = "https://rabbitstream.net"
+ override val requiresReferer = false
+ open val embed = "ajax/embed-4"
+ open val key = "https://raw.githubusercontent.com/enimax-anime/key/e4/key.txt"
+ private var rawKey: String? = null
+
+ override suspend fun getUrl(
+ url: String,
+ referer: String?,
+ subtitleCallback: (SubtitleFile) -> Unit,
+ callback: (ExtractorLink) -> Unit
+ ) {
+ val id = url.substringAfterLast("/").substringBefore("?")
+
+ val response = app.get(
+ "$mainUrl/$embed/getSources?id=$id",
+ referer = mainUrl,
+ headers = mapOf("X-Requested-With" to "XMLHttpRequest")
+ )
+
+ val encryptedMap = response.parsedSafe()
+ val sources = encryptedMap?.sources
+ val decryptedSources = if (sources == null || encryptedMap.encrypted == false) {
+ response.parsedSafe()
+ } else {
+ val (key, encData) = extractRealKey(sources, getRawKey())
+ val decrypted = decryptMapped>(encData, key)
+ SourcesResponses(
+ sources = decrypted,
+ tracks = encryptedMap.tracks
+ )
+ }
+
+ decryptedSources?.sources?.map { source ->
+ M3u8Helper.generateM3u8(
+ name,
+ source?.file ?: return@map,
+ "$mainUrl/",
+ ).forEach(callback)
+ }
+
+ decryptedSources?.tracks?.map { track ->
+ subtitleCallback.invoke(
+ SubtitleFile(
+ track?.label ?: "",
+ track?.file ?: return@map
+ )
+ )
+ }
+
+ }
+
+ private suspend fun getRawKey(): String = rawKey ?: app.get(key).text.also { rawKey = it }
+
+ private fun extractRealKey(originalString: String?, stops: String): Pair {
+ val table = parseJson>>(stops)
+ val decryptedKey = StringBuilder()
+ var offset = 0
+ var encryptedString = originalString
+
+ table.forEach { (start, end) ->
+ decryptedKey.append(encryptedString?.substring(start - offset, end - offset))
+ encryptedString = encryptedString?.substring(
+ 0,
+ start - offset
+ ) + encryptedString?.substring(end - offset)
+ offset += end - start
+ }
+ return decryptedKey.toString() to encryptedString.toString()
+ }
+
+ private inline fun decryptMapped(input: String, key: String): T? {
+ val decrypt = decrypt(input, key)
+ return AppUtils.tryParseJson(decrypt)
+ }
+
+ private fun decrypt(input: String, key: String): String {
+ return decryptSourceUrl(
+ generateKey(
+ base64DecodeArray(input).copyOfRange(8, 16),
+ key.toByteArray()
+ ), input
+ )
+ }
+
+ private fun generateKey(salt: ByteArray, secret: ByteArray): ByteArray {
+ var key = md5(secret + salt)
+ var currentKey = key
+ while (currentKey.size < 48) {
+ key = md5(key + secret + salt)
+ currentKey += key
+ }
+ return currentKey
+ }
+
+ private fun md5(input: ByteArray): ByteArray {
+ return MessageDigest.getInstance("MD5").digest(input)
+ }
+
+ private fun decryptSourceUrl(decryptionKey: ByteArray, sourceUrl: String): String {
+ val cipherData = base64DecodeArray(sourceUrl)
+ val encrypted = cipherData.copyOfRange(16, cipherData.size)
+ val aesCBC = Cipher.getInstance("AES/CBC/PKCS5Padding")
+ aesCBC.init(
+ Cipher.DECRYPT_MODE,
+ SecretKeySpec(decryptionKey.copyOfRange(0, 32), "AES"),
+ IvParameterSpec(decryptionKey.copyOfRange(32, decryptionKey.size))
+ )
+ val decryptedData = aesCBC?.doFinal(encrypted) ?: throw ErrorLoadingException("Cipher not found")
+ return String(decryptedData, StandardCharsets.UTF_8)
+ }
+
+ data class Tracks(
+ @JsonProperty("file") val file: String? = null,
+ @JsonProperty("label") val label: String? = null,
+ @JsonProperty("kind") val kind: String? = null,
+ )
+
+ data class Sources(
+ @JsonProperty("file") val file: String? = null,
+ @JsonProperty("type") val type: String? = null,
+ @JsonProperty("label") val label: String? = null,
+ )
+
+ data class SourcesResponses(
+ @JsonProperty("sources") val sources: List? = emptyList(),
+ @JsonProperty("tracks") val tracks: List? = emptyList(),
+ )
+
+ data class SourcesEncrypted(
+ @JsonProperty("sources") val sources: String? = null,
+ @JsonProperty("encrypted") val encrypted: Boolean? = null,
+ @JsonProperty("tracks") val tracks: List? = emptyList(),
+ )
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt
index ed190bcc..83c61542 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt
@@ -428,7 +428,10 @@ val extractorApis: MutableList = arrayListOf(
Cda(),
Dailymotion(),
ByteShare(),
- Ztreamhub()
+ Ztreamhub(),
+ Rabbitstream(),
+ Dokicloud(),
+ Megacloud(),
)