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*/
|
/**Used for testing and can be used to disable the providers if WebView is not available*/
|
||||||
open val usesWebView = false
|
open val usesWebView = false
|
||||||
|
|
||||||
|
/** Determines which plugin a given provider is from */
|
||||||
|
var sourcePlugin: String? = null
|
||||||
|
|
||||||
open val hasMainPage = false
|
open val hasMainPage = false
|
||||||
open val hasQuickSearch = false
|
open val hasQuickSearch = false
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,13 @@ package com.lagradost.cloudstream3.plugins
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.res.Resources
|
import android.content.res.Resources
|
||||||
import kotlin.Throws
|
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 {
|
abstract class Plugin {
|
||||||
/**
|
/**
|
||||||
|
@ -13,6 +20,33 @@ abstract class Plugin {
|
||||||
open fun load(context: Context) {
|
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 {
|
class Manifest {
|
||||||
var name: String? = null
|
var name: String? = null
|
||||||
var pluginClassName: 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.REPOSITORIES_KEY
|
||||||
import com.lagradost.cloudstream3.ui.settings.extensions.RepositoryData
|
import com.lagradost.cloudstream3.ui.settings.extensions.RepositoryData
|
||||||
import com.lagradost.cloudstream3.utils.VideoDownloadManager.sanitizeFilename
|
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.Mutex
|
||||||
import kotlinx.coroutines.sync.withLock
|
import kotlinx.coroutines.sync.withLock
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
@ -99,8 +103,7 @@ object PluginManager {
|
||||||
|
|
||||||
private val classLoaders: MutableMap<PathClassLoader, Plugin> =
|
private val classLoaders: MutableMap<PathClassLoader, Plugin> =
|
||||||
HashMap<PathClassLoader, Plugin>()
|
HashMap<PathClassLoader, Plugin>()
|
||||||
|
|
||||||
private val failedToLoad: MutableMap<File, Any> = LinkedHashMap()
|
|
||||||
private var loadedLocalPlugins = false
|
private var loadedLocalPlugins = false
|
||||||
private val gson = Gson()
|
private val gson = Gson()
|
||||||
|
|
||||||
|
@ -112,6 +115,8 @@ object PluginManager {
|
||||||
file,
|
file,
|
||||||
PluginData(name, null, false, file.absolutePath, PLUGIN_VERSION_NOT_SET)
|
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()
|
val sortedPlugins = dir.listFiles()
|
||||||
// Always sort plugins alphabetically for reproducible results
|
// Always sort plugins alphabetically for reproducible results
|
||||||
|
|
||||||
|
Log.d(TAG, "Files in '${LOCAL_PLUGINS_PATH}' folder: ${sortedPlugins}")
|
||||||
|
|
||||||
sortedPlugins?.sortedBy { it.name }?.apmap { file ->
|
sortedPlugins?.sortedBy { it.name }?.apmap { file ->
|
||||||
maybeLoadPlugin(activity, file)
|
maybeLoadPlugin(activity, file)
|
||||||
}
|
}
|
||||||
|
@ -217,7 +224,6 @@ object PluginManager {
|
||||||
var manifest: Plugin.Manifest
|
var manifest: Plugin.Manifest
|
||||||
loader.getResourceAsStream("manifest.json").use { stream ->
|
loader.getResourceAsStream("manifest.json").use { stream ->
|
||||||
if (stream == null) {
|
if (stream == null) {
|
||||||
failedToLoad[file] = "No manifest found"
|
|
||||||
Log.e(TAG, "Failed to load plugin $fileName: No manifest found")
|
Log.e(TAG, "Failed to load plugin $fileName: No manifest found")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -263,7 +269,6 @@ object PluginManager {
|
||||||
Log.i(TAG, "Loaded plugin ${data.internalName} successfully")
|
Log.i(TAG, "Loaded plugin ${data.internalName} successfully")
|
||||||
true
|
true
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
failedToLoad[file] = e
|
|
||||||
Log.e(TAG, "Failed to load $file: ${Log.getStackTraceString(e)}")
|
Log.e(TAG, "Failed to load $file: ${Log.getStackTraceString(e)}")
|
||||||
showToast(
|
showToast(
|
||||||
activity,
|
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.
|
* Spits out a unique and safe filename based on name.
|
||||||
* Used for repo folders (using repo url) and plugin file names (using internalName)
|
* Used for repo folders (using repo url) and plugin file names (using internalName)
|
||||||
|
@ -309,6 +334,7 @@ object PluginManager {
|
||||||
?: return false
|
?: return false
|
||||||
return try {
|
return try {
|
||||||
if (File(data.filePath).delete()) {
|
if (File(data.filePath).delete()) {
|
||||||
|
unloadPlugin(data.filePath)
|
||||||
deletePluginData(data)
|
deletePluginData(data)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
|
@ -247,6 +247,9 @@ abstract class ExtractorApi {
|
||||||
abstract val mainUrl: String
|
abstract val mainUrl: String
|
||||||
abstract val requiresReferer: Boolean
|
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>? {
|
//suspend fun getSafeUrl(url: String, referer: String? = null): List<ExtractorLink>? {
|
||||||
// return suspendSafeApiCall { getUrl(url, referer) }
|
// return suspendSafeApiCall { getUrl(url, referer) }
|
||||||
//}
|
//}
|
||||||
|
|
Loading…
Reference in a new issue