forked from recloudstream/cloudstream
add plugin unloading
This commit is contained in:
parent
08d7f3444f
commit
bceeec849d
4 changed files with 70 additions and 4 deletions
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
@ -100,7 +104,6 @@ object PluginManager {
|
|||
private val classLoaders: MutableMap<PathClassLoader, Plugin> =
|
||||
HashMap<PathClassLoader, Plugin>()
|
||||
|
||||
private val failedToLoad: MutableMap<File, Any> = 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
|
||||
}
|
||||
|
|
|
@ -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<ExtractorLink>? {
|
||||
// return suspendSafeApiCall { getUrl(url, referer) }
|
||||
//}
|
||||
|
|
Loading…
Reference in a new issue