diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt index 473dc5a9..0de2df7d 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt @@ -342,6 +342,9 @@ abstract class MainAPI { /**Used for testing and can be used to disable the providers if WebView is not available*/ open val usesWebView = false + /** Determines which plugin a given provider is from */ + var sourcePlugin: String? = null + open val hasMainPage = false open val hasQuickSearch = false diff --git a/app/src/main/java/com/lagradost/cloudstream3/plugins/Plugin.kt b/app/src/main/java/com/lagradost/cloudstream3/plugins/Plugin.kt index 5ed1931b..9a3dd23b 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/plugins/Plugin.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/plugins/Plugin.kt @@ -3,6 +3,13 @@ package com.lagradost.cloudstream3.plugins import android.content.Context import android.content.res.Resources import kotlin.Throws +import com.lagradost.cloudstream3.MainAPI +import com.lagradost.cloudstream3.APIHolder +import com.lagradost.cloudstream3.utils.ExtractorApi +import com.lagradost.cloudstream3.utils.extractorApis +import android.util.Log + +const val PLUGIN_TAG = "PluginInstance" abstract class Plugin { /** @@ -13,6 +20,33 @@ abstract class Plugin { open fun load(context: Context) { } + /** + * Called when your Plugin is being unloaded + */ + @Throws(Throwable::class) + open fun beforeUnload() { + } + + /** + * Used to register providers instances of MainAPI + * @param element MainAPI provider you want to register + */ + fun registerMainAPI(element: MainAPI) { + Log.i(PLUGIN_TAG, "Adding ${element.name} (${element.mainUrl}) MainAPI") + element.sourcePlugin = this.`__filename` + APIHolder.allProviders.add(element) + } + + /** + * Used to register extractor instances of ExtractorApi + * @param element ExtractorApi provider you want to register + */ + fun registerExtractorAPI(element: ExtractorApi) { + Log.i(PLUGIN_TAG, "Adding ${element.name} (${element.mainUrl}) ExtractorApi") + element.sourcePlugin = this.`__filename` + extractorApis.add(element) + } + class Manifest { var name: String? = null var pluginClassName: String? = null 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 25e684d6..f9aa7cdd 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt @@ -23,6 +23,10 @@ import com.lagradost.cloudstream3.plugins.RepositoryManager.getRepoPlugins import com.lagradost.cloudstream3.ui.settings.extensions.REPOSITORIES_KEY import com.lagradost.cloudstream3.ui.settings.extensions.RepositoryData import com.lagradost.cloudstream3.utils.VideoDownloadManager.sanitizeFilename +import com.lagradost.cloudstream3.APIHolder +import com.lagradost.cloudstream3.MainAPI +import com.lagradost.cloudstream3.utils.ExtractorApi +import com.lagradost.cloudstream3.utils.extractorApis import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import java.io.File @@ -99,8 +103,7 @@ object PluginManager { private val classLoaders: MutableMap = HashMap() - - private val failedToLoad: MutableMap = LinkedHashMap() + private var loadedLocalPlugins = false private val gson = Gson() @@ -112,6 +115,8 @@ object PluginManager { file, PluginData(name, null, false, file.absolutePath, PLUGIN_VERSION_NOT_SET) ) + } else { + Log.i(TAG, "Skipping invalid plugin file: $file") } } @@ -197,6 +202,8 @@ object PluginManager { val sortedPlugins = dir.listFiles() // Always sort plugins alphabetically for reproducible results + Log.d(TAG, "Files in '${LOCAL_PLUGINS_PATH}' folder: ${sortedPlugins}") + sortedPlugins?.sortedBy { it.name }?.apmap { file -> maybeLoadPlugin(activity, file) } @@ -217,7 +224,6 @@ object PluginManager { var manifest: Plugin.Manifest loader.getResourceAsStream("manifest.json").use { stream -> if (stream == null) { - failedToLoad[file] = "No manifest found" Log.e(TAG, "Failed to load plugin $fileName: No manifest found") return false } @@ -263,7 +269,6 @@ object PluginManager { Log.i(TAG, "Loaded plugin ${data.internalName} successfully") true } catch (e: Throwable) { - failedToLoad[file] = e Log.e(TAG, "Failed to load $file: ${Log.getStackTraceString(e)}") showToast( activity, @@ -274,6 +279,26 @@ object PluginManager { } } + private suspend fun unloadPlugin(absolutePath: String) { + var plugin = plugins.get(absolutePath) + if (plugin == null) { + Log.w(TAG, "Couldn't find plugin $absolutePath") + return + } + + try { + plugin.beforeUnload() + } catch (e: Throwable) { + Log.e(TAG, "Failed to run beforeUnload $absolutePath: ${Log.getStackTraceString(e)}") + } + + // remove all registered apis + APIHolder.allProviders.removeIf { provider: MainAPI -> provider.sourcePlugin == plugin.`__filename` } + extractorApis.removeIf { provider: ExtractorApi -> provider.sourcePlugin == plugin.`__filename` } + + plugins.remove(absolutePath) + } + /** * Spits out a unique and safe filename based on name. * Used for repo folders (using repo url) and plugin file names (using internalName) @@ -309,6 +334,7 @@ object PluginManager { ?: return false return try { if (File(data.filePath).delete()) { + unloadPlugin(data.filePath) deletePluginData(data) return true } 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 e2842fc0..16645547 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt @@ -247,6 +247,9 @@ abstract class ExtractorApi { abstract val mainUrl: String abstract val requiresReferer: Boolean + /** Determines which plugin a given extractor is from */ + var sourcePlugin: String? = null + //suspend fun getSafeUrl(url: String, referer: String? = null): List? { // return suspendSafeApiCall { getUrl(url, referer) } //}