diff --git a/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt index 49143498..8f22c01a 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt @@ -166,6 +166,8 @@ object CommonActivity { "Light" -> R.style.LightMode "Amoled" -> R.style.AmoledMode "AmoledLight" -> R.style.AmoledModeLight + "Monet" -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) + R.style.MonetMode else R.style.AppTheme else -> R.style.AppTheme } @@ -186,6 +188,10 @@ object CommonActivity { "Banana" -> R.style.OverlayPrimaryColorBanana "Party" -> R.style.OverlayPrimaryColorParty "Pink" -> R.style.OverlayPrimaryColorPink + "Monet" -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) + R.style.OverlayPrimaryColorMonet else R.style.OverlayPrimaryColorNormal + "Monet2" -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) + R.style.OverlayPrimaryColorMonetTwo else R.style.OverlayPrimaryColorNormal else -> R.style.OverlayPrimaryColorNormal } act.theme.applyStyle(currentTheme, true) diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/VidSrcExtractor.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/VidSrcExtractor.kt index 7b087157..63634704 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/VidSrcExtractor.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/VidSrcExtractor.kt @@ -3,10 +3,9 @@ package com.lagradost.cloudstream3.extractors import com.lagradost.cloudstream3.SubtitleFile import com.lagradost.cloudstream3.apmap import com.lagradost.cloudstream3.app -import com.lagradost.cloudstream3.utils.ExtractorApi -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.M3u8Helper -import com.lagradost.cloudstream3.utils.loadExtractor +import com.lagradost.cloudstream3.utils.* +import kotlinx.coroutines.delay +import java.net.URI class VidSrcExtractor2 : VidSrcExtractor() { override val mainUrl = "https://vidsrc.me/embed" @@ -27,6 +26,25 @@ open class VidSrcExtractor : ExtractorApi() { override val mainUrl = "$absoluteUrl/embed" override val requiresReferer = false + companion object { + /** Infinite function to validate the vidSrc pass */ + suspend fun validatePass(url: String) { + val uri = URI(url) + val host = uri.host + + // Basically turn https://tm3p.vidsrc.stream/ -> https://vidsrc.stream/ + val referer = host.split(".").let { + val size = it.size + "https://" + it.subList(maxOf(0, size - 2), size).joinToString(".") + "/" + } + + while (true) { + app.get(url, referer = referer) + delay(60_000) + } + } + } + override suspend fun getUrl( url: String, referer: String?, @@ -40,7 +58,10 @@ open class VidSrcExtractor : ExtractorApi() { val datahash = it.attr("data-hash") if (datahash.isNotBlank()) { val links = try { - app.get("$absoluteUrl/src/$datahash", referer = "https://source.vidsrc.me/").url + app.get( + "$absoluteUrl/src/$datahash", + referer = "https://source.vidsrc.me/" + ).url } catch (e: Exception) { "" } @@ -54,11 +75,28 @@ open class VidSrcExtractor : ExtractorApi() { val srcresponse = app.get(server, referer = absoluteUrl).text val m3u8Regex = Regex("((https:|http:)//.*\\.m3u8)") val srcm3u8 = m3u8Regex.find(srcresponse)?.value ?: return@apmap - M3u8Helper.generateM3u8( - name, - srcm3u8, - absoluteUrl - ).forEach(callback) + val passRegex = Regex("""['"](.*set_pass[^"']*)""") + val pass = passRegex.find(srcresponse)?.groupValues?.get(1)?.replace( + Regex("""^//"""), "https://" + ) + + callback.invoke( + ExtractorLink( + this.name, + this.name, + srcm3u8, + this.mainUrl, + Qualities.Unknown.value, + extractorData = pass, + isM3u8 = true + ) + ) + +// M3u8Helper.generateM3u8( +// name, +// srcm3u8, +// absoluteUrl +// ).forEach(callback) } else { loadExtractor(linkfixed, url, subtitleCallback, callback) } diff --git a/app/src/main/java/com/lagradost/cloudstream3/mvvm/ArchComponentExt.kt b/app/src/main/java/com/lagradost/cloudstream3/mvvm/ArchComponentExt.kt index 5c3276fa..e5c03d64 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/mvvm/ArchComponentExt.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/mvvm/ArchComponentExt.kt @@ -15,6 +15,7 @@ import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext const val DEBUG_EXCEPTION = "THIS IS A DEBUG EXCEPTION!" +const val DEBUG_PRINT = "DEBUG PRINT" class DebugException(message: String) : Exception("$DEBUG_EXCEPTION\n$message") @@ -24,6 +25,12 @@ inline fun debugException(message: () -> String) { } } +inline fun debugPrint(tag: String = DEBUG_PRINT, message: () -> String) { + if (BuildConfig.DEBUG) { + Log.d(tag, message.invoke()) + } +} + inline fun debugWarning(message: () -> String) { if (BuildConfig.DEBUG) { logError(DebugException(message.invoke())) diff --git a/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt b/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt index c4d1f66f..bf7e694c 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt @@ -8,12 +8,10 @@ import android.content.res.Resources import android.os.Environment import android.widget.Toast import android.content.Context -import android.content.Intent import android.os.Build import android.util.Log import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat -import androidx.core.net.toUri import com.fasterxml.jackson.annotation.JsonProperty import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.AcraApplication.Companion.getKey @@ -28,6 +26,7 @@ import com.lagradost.cloudstream3.ui.settings.extensions.RepositoryData import com.lagradost.cloudstream3.utils.VideoDownloadManager.sanitizeFilename import com.lagradost.cloudstream3.APIHolder.removePluginMapping import com.lagradost.cloudstream3.MainActivity.Companion.afterPluginsLoadedEvent +import com.lagradost.cloudstream3.mvvm.debugPrint import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.plugins.RepositoryManager.PREBUILT_REPOSITORIES import com.lagradost.cloudstream3.utils.Coroutines.ioSafe @@ -37,7 +36,6 @@ import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute import com.lagradost.cloudstream3.utils.extractorApis import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock -import org.acra.log.debug import java.io.File import java.io.InputStreamReader import java.util.* @@ -223,13 +221,14 @@ object PluginManager { // Iterates over all offline plugins, compares to remote repo and returns the plugins which are outdated val outdatedPlugins = getPluginsOnline().map { savedData -> - onlinePlugins.filter { onlineData -> savedData.internalName == onlineData.second.internalName } + onlinePlugins + .filter { onlineData -> savedData.internalName == onlineData.second.internalName } .map { onlineData -> OnlinePluginData(savedData, onlineData) } }.flatten().distinctBy { it.onlineData.second.url } - debug { + debugPrint { "Outdated plugins: ${outdatedPlugins.filter { it.isOutdated }}" } @@ -244,7 +243,7 @@ object PluginManager { activity, pluginData.onlineData.second.url, pluginData.savedData.internalName, - pluginData.onlineData.first + File(pluginData.savedData.filePath) ).let { success -> if (success) updatedPlugins.add(pluginData.onlineData.second.name) @@ -417,24 +416,45 @@ object PluginManager { ) + "." + name.hashCode() } + + /** + * Used for fresh installs + * */ suspend fun downloadAndLoadPlugin( activity: Activity, pluginUrl: String, internalName: String, repositoryUrl: String ): Boolean { - try { - val folderName = getPluginSanitizedFileName(repositoryUrl) // Guaranteed unique - val fileName = getPluginSanitizedFileName(internalName) - unloadPlugin("${activity.filesDir}/${ONLINE_PLUGINS_FOLDER}/${folderName}/$fileName.cs3") + val folderName = getPluginSanitizedFileName(repositoryUrl) // Guaranteed unique + val fileName = getPluginSanitizedFileName(internalName) + val file = File("${activity.filesDir}/${ONLINE_PLUGINS_FOLDER}/${folderName}/$fileName.cs3") + downloadAndLoadPlugin(activity, pluginUrl, internalName, file) + return true + } - Log.d(TAG, "Downloading plugin: $pluginUrl to $folderName/$fileName") + /** + * Used for updates. + * + * Uses a file instead of repository url, as extensions can get moved it is better to directly + * update the files instead of getting the filepath from repo url. + * */ + private suspend fun downloadAndLoadPlugin( + activity: Activity, + pluginUrl: String, + internalName: String, + file: File, + ): Boolean { + try { + unloadPlugin(file.absolutePath) + + Log.d(TAG, "Downloading plugin: $pluginUrl to ${file.absolutePath}") // The plugin file needs to be salted with the repository url hash as to allow multiple repositories with the same internal plugin names - val file = downloadPluginToFile(activity, pluginUrl, fileName, folderName) + val newFile = downloadPluginToFile(pluginUrl, file) return loadPlugin( activity, - file ?: return false, - PluginData(internalName, pluginUrl, true, file.absolutePath, PLUGIN_VERSION_NOT_SET) + newFile ?: return false, + PluginData(internalName, pluginUrl, true, newFile.absolutePath, PLUGIN_VERSION_NOT_SET) ) } catch (e: Exception) { logError(e) @@ -468,7 +488,8 @@ object PluginManager { // the NotificationChannel class is new and not in the support library if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val name = EXTENSIONS_CHANNEL_NAME //getString(R.string.channel_name) - val descriptionText = EXTENSIONS_CHANNEL_DESCRIPT//getString(R.string.channel_description) + val descriptionText = + EXTENSIONS_CHANNEL_DESCRIPT//getString(R.string.channel_description) val importance = NotificationManager.IMPORTANCE_LOW val channel = NotificationChannel(EXTENSIONS_CHANNEL_ID, name, importance).apply { description = descriptionText @@ -479,10 +500,11 @@ object PluginManager { notificationManager.createNotificationChannel(channel) } } + private fun createNotification( context: Context, extensionNames: List - ): Notification? { + ): Notification? { try { if (extensionNames.isEmpty()) return null @@ -497,8 +519,10 @@ object PluginManager { .setColor(context.colorFromAttribute(R.attr.colorPrimary)) .setContentTitle(context.getString(R.string.plugins_updated, extensionNames.size)) .setSmallIcon(R.drawable.ic_baseline_extension_24) - .setStyle(NotificationCompat.BigTextStyle() - .bigText(content)) + .setStyle( + NotificationCompat.BigTextStyle() + .bigText(content) + ) .setContentText(content) if (!hasCreatedNotChanel) { @@ -508,7 +532,7 @@ object PluginManager { val notification = builder.build() with(NotificationManagerCompat.from(context)) { // notificationId is a unique int for each notification that you must define - notify((System.currentTimeMillis()/1000).toInt(), notification) + notify((System.currentTimeMillis() / 1000).toInt(), notification) } return notification } catch (e: Exception) { diff --git a/app/src/main/java/com/lagradost/cloudstream3/plugins/RepositoryManager.kt b/app/src/main/java/com/lagradost/cloudstream3/plugins/RepositoryManager.kt index ee0b60b8..e3203787 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/plugins/RepositoryManager.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/plugins/RepositoryManager.kt @@ -84,7 +84,7 @@ object RepositoryManager { // Normal parsed function not working? // return response.parsedSafe() tryParseJson>(response.text)?.toList() ?: emptyList() - } catch (t : Throwable) { + } catch (t: Throwable) { logError(t) emptyList() } @@ -102,9 +102,27 @@ object RepositoryManager { }.flatten() } + suspend fun downloadPluginToFile( + pluginUrl: String, + file: File + ): File? { + return suspendSafeApiCall { + file.mkdirs() + + // Overwrite if exists + if (file.exists()) { file.delete() } + file.createNewFile() + + val body = app.get(pluginUrl).okhttpResponse.body + write(body.byteStream(), file.outputStream()) + file + } + } + suspend fun downloadPluginToFile( context: Context, pluginUrl: String, + /** Filename without .cs3 */ fileName: String, folder: String ): File? { diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUI.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUI.kt index 9ad3ee8f..e2fd24ca 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUI.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUI.kt @@ -1,5 +1,6 @@ package com.lagradost.cloudstream3.ui.settings +import android.os.Build import android.os.Bundle import android.view.View import androidx.preference.PreferenceFragmentCompat @@ -83,8 +84,20 @@ class SettingsUI : PreferenceFragmentCompat() { } getPref(R.string.app_theme_key)?.setOnPreferenceClickListener { - val prefNames = resources.getStringArray(R.array.themes_names) - val prefValues = resources.getStringArray(R.array.themes_names_values) + val prefNames = resources.getStringArray(R.array.themes_names).toMutableList() + val prefValues = resources.getStringArray(R.array.themes_names_values).toMutableList() + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) { // remove monet on android 11 and less + val toRemove = prefValues + .mapIndexed { idx, s -> if (s.startsWith("Monet")) idx else null } + .filterNotNull() + var offset = 0 + toRemove.forEach { idx -> + prefNames.removeAt(idx - offset) + prefValues.removeAt(idx - offset) + offset += 1 + } + } val currentLayout = settingsManager.getString(getString(R.string.app_theme_key), prefValues.first()) @@ -107,8 +120,20 @@ class SettingsUI : PreferenceFragmentCompat() { return@setOnPreferenceClickListener true } getPref(R.string.primary_color_key)?.setOnPreferenceClickListener { - val prefNames = resources.getStringArray(R.array.themes_overlay_names) - val prefValues = resources.getStringArray(R.array.themes_overlay_names_values) + val prefNames = resources.getStringArray(R.array.themes_overlay_names).toMutableList() + val prefValues = resources.getStringArray(R.array.themes_overlay_names_values).toMutableList() + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) { // remove monet on android 11 and less + val toRemove = prefValues + .mapIndexed { idx, s -> if (s.startsWith("Monet")) idx else null } + .filterNotNull() + var offset = 0 + toRemove.forEach { idx -> + prefNames.removeAt(idx - offset) + prefValues.removeAt(idx - offset) + offset += 1 + } + } val currentLayout = settingsManager.getString(getString(R.string.primary_color_key), prefValues.first()) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/ExtensionsViewModel.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/ExtensionsViewModel.kt index b5f82ae8..897e7366 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/ExtensionsViewModel.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/ExtensionsViewModel.kt @@ -3,21 +3,19 @@ package com.lagradost.cloudstream3.ui.settings.extensions import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope import com.fasterxml.jackson.annotation.JsonProperty import com.lagradost.cloudstream3.AcraApplication.Companion.getKey import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.apmap import com.lagradost.cloudstream3.mvvm.Some import com.lagradost.cloudstream3.mvvm.debugAssert -import com.lagradost.cloudstream3.mvvm.launchSafe import com.lagradost.cloudstream3.plugins.PluginManager import com.lagradost.cloudstream3.plugins.PluginManager.getPluginsOnline import com.lagradost.cloudstream3.plugins.RepositoryManager import com.lagradost.cloudstream3.plugins.RepositoryManager.PREBUILT_REPOSITORIES import com.lagradost.cloudstream3.ui.result.UiText import com.lagradost.cloudstream3.ui.result.txt -import kotlinx.coroutines.launch +import com.lagradost.cloudstream3.utils.Coroutines.ioSafe data class RepositoryData( @JsonProperty("name") val name: String, @@ -46,7 +44,8 @@ class ExtensionsViewModel : ViewModel() { val pluginStats: LiveData> = _pluginStats //TODO CACHE GET REQUESTS - fun loadStats() = viewModelScope.launchSafe { + // DO not use viewModelScope.launchSafe, it will ANR on slow internet + fun loadStats() = ioSafe { val urls = (getKey>(REPOSITORIES_KEY) ?: emptyArray()) + PREBUILT_REPOSITORIES @@ -61,6 +60,7 @@ class ExtensionsViewModel : ViewModel() { PluginManager.OnlinePluginData(savedData, onlineData) } }.flatten().distinctBy { it.onlineData.second.url } + val total = onlinePlugins.count() val disabled = outdatedPlugins.count { it.isDisabled } val downloadedTotal = outdatedPlugins.count() diff --git a/app/src/main/res/values-es/array.xml b/app/src/main/res/values-es/array.xml index 658ba7ae..ae986642 100644 --- a/app/src/main/res/values-es/array.xml +++ b/app/src/main/res/values-es/array.xml @@ -14,19 +14,6 @@ @id/cast_button_type_forward_30_seconds - - Todos - Películas y TV - Anime - Documental - - - 0 - 1 - 2 - 3 - - @string/resolution_and_title @string/title @@ -222,6 +209,8 @@ Banana Fiesta Dolor rosa + Material You + Material You (Secondary) Normal @@ -240,19 +229,24 @@ Banana Party Pink + Monet + Monet2 + Oscuro Gris Amoled Destello + Material You AmoledLight Black Amoled Light + Monet diff --git a/app/src/main/res/values-pl/array.xml b/app/src/main/res/values-pl/array.xml index c40b7f87..3fe30d95 100644 --- a/app/src/main/res/values-pl/array.xml +++ b/app/src/main/res/values-pl/array.xml @@ -218,6 +218,8 @@ Bananowy Łososiowy Świnko peppowy + Material You + Material You (drugorzędny) Normal @@ -236,19 +238,24 @@ Banana Party Pink + Monet + Monet2 + Ciemny Szary Amoled Flashbang + Material You AmoledLight Black Amoled Light + Monet diff --git a/app/src/main/res/values-tr/array.xml b/app/src/main/res/values-tr/array.xml index ec9acd0e..dbb17d36 100644 --- a/app/src/main/res/values-tr/array.xml +++ b/app/src/main/res/values-tr/array.xml @@ -14,19 +14,6 @@ @id/cast_button_type_forward_30_seconds - - Hepsi - Film ve Dizi - Anime - Belgesel - - - 0 - 1 - 2 - 3 - - @string/resolution_and_title @string/title @@ -222,6 +209,8 @@ Muz Parti Pembe + Material You + Material You (Secondary) Normal @@ -240,19 +229,24 @@ Banana Party Pink + Monet + Monet2 + Koyu Gri Amoled Flaş Bombası + Material You AmoledLight Black Amoled Light + Monet diff --git a/app/src/main/res/values-vi/array.xml b/app/src/main/res/values-vi/array.xml index 5e70223b..5fee2d29 100644 --- a/app/src/main/res/values-vi/array.xml +++ b/app/src/main/res/values-vi/array.xml @@ -14,18 +14,6 @@ @id/cast_button_type_forward_30_seconds - - Tất cả - Phim lẻ và Phim bộ - Anime - Phim tài liệu - - - 0 - 1 - 2 - 3 - @string/resolution_and_title @@ -222,6 +210,8 @@ Vàng Hồng Hồng đậm + Material You + Material You (Secondary) Normal @@ -240,19 +230,24 @@ Banana Party Pink + Monet + Monet2 + Tối Xám Amoled Sáng + Material You AmoledLight Black Amoled Light + Monet diff --git a/app/src/main/res/values/array.xml b/app/src/main/res/values/array.xml index 13d4f2dc..fedc4219 100644 --- a/app/src/main/res/values/array.xml +++ b/app/src/main/res/values/array.xml @@ -228,6 +228,8 @@ Banana Party Pink Pain + Material You + Material You (Secondary) Normal @@ -246,6 +248,8 @@ Banana Party Pink + Monet + Monet2 @@ -253,12 +257,14 @@ Gray Amoled Flashbang + Material You AmoledLight Black Amoled Light + Monet diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 2fb9b5b4..9840cb80 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -95,6 +95,16 @@ #000 + + + + + +