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? {