mirror of
https://github.com/recloudstream/cloudstream.git
synced 2025-05-17 19:25:55 +08:00
Make the plugin part crossplatform (#1527)
This commit is contained in:
@ -2,56 +2,21 @@ 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
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import com.lagradost.cloudstream3.actions.VideoClickAction
|
||||
import com.lagradost.cloudstream3.actions.VideoClickActionHolder
|
||||
import kotlin.Throws
|
||||
|
||||
const val PLUGIN_TAG = "PluginInstance"
|
||||
|
||||
abstract class Plugin {
|
||||
abstract class Plugin : BasePlugin() {
|
||||
/**
|
||||
* Called when your Plugin is loaded
|
||||
* @param context Context
|
||||
*/
|
||||
@Throws(Throwable::class)
|
||||
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
|
||||
// Race condition causing which would case duplicates if not for distinctBy
|
||||
synchronized(APIHolder.allProviders) {
|
||||
APIHolder.allProviders.add(element)
|
||||
}
|
||||
APIHolder.addPluginMapping(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)
|
||||
// If not overridden by an extension then try the cross-platform load()
|
||||
load()
|
||||
}
|
||||
|
||||
/**
|
||||
@ -66,27 +31,10 @@ abstract class Plugin {
|
||||
}
|
||||
}
|
||||
|
||||
class Manifest {
|
||||
@JsonProperty("name")
|
||||
var name: String? = null
|
||||
@JsonProperty("pluginClassName")
|
||||
var pluginClassName: String? = null
|
||||
@JsonProperty("version")
|
||||
var version: Int? = null
|
||||
@JsonProperty("requiresResources")
|
||||
var requiresResources: Boolean = false
|
||||
}
|
||||
|
||||
/**
|
||||
* This will contain your resources if you specified requiresResources in gradle
|
||||
*/
|
||||
var resources: Resources? = null
|
||||
/** Full file path to the plugin. */
|
||||
@Deprecated("Renamed to `filename` to follow conventions", replaceWith = ReplaceWith("filename"))
|
||||
var __filename: String?
|
||||
get() = filename
|
||||
set(value) {filename = value}
|
||||
var filename: String? = null
|
||||
|
||||
/**
|
||||
* This will add a button in the settings allowing you to add custom settings
|
||||
|
@ -172,15 +172,15 @@ object PluginManager {
|
||||
var currentlyLoading: String? = null
|
||||
|
||||
// Maps filepath to plugin
|
||||
val plugins: MutableMap<String, Plugin> =
|
||||
LinkedHashMap<String, Plugin>()
|
||||
val plugins: MutableMap<String, BasePlugin> =
|
||||
LinkedHashMap<String, BasePlugin>()
|
||||
|
||||
// Maps urls to plugin
|
||||
val urlPlugins: MutableMap<String, Plugin> =
|
||||
LinkedHashMap<String, Plugin>()
|
||||
val urlPlugins: MutableMap<String, BasePlugin> =
|
||||
LinkedHashMap<String, BasePlugin>()
|
||||
|
||||
private val classLoaders: MutableMap<PathClassLoader, Plugin> =
|
||||
HashMap<PathClassLoader, Plugin>()
|
||||
private val classLoaders: MutableMap<PathClassLoader, BasePlugin> =
|
||||
HashMap<PathClassLoader, BasePlugin>()
|
||||
|
||||
var loadedLocalPlugins = false
|
||||
private set
|
||||
@ -530,7 +530,7 @@ object PluginManager {
|
||||
}
|
||||
|
||||
val loader = PathClassLoader(filePath, context.classLoader)
|
||||
var manifest: Plugin.Manifest
|
||||
var manifest: BasePlugin.Manifest
|
||||
loader.getResourceAsStream("manifest.json").use { stream ->
|
||||
if (stream == null) {
|
||||
Log.e(TAG, "Failed to load plugin $fileName: No manifest found")
|
||||
@ -539,7 +539,7 @@ object PluginManager {
|
||||
InputStreamReader(stream).use { reader ->
|
||||
manifest = gson.fromJson(
|
||||
reader,
|
||||
Plugin.Manifest::class.java
|
||||
BasePlugin.Manifest::class.java
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -553,9 +553,9 @@ object PluginManager {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val pluginClass: Class<*> =
|
||||
loader.loadClass(manifest.pluginClassName) as Class<out Plugin?>
|
||||
val pluginInstance: Plugin =
|
||||
pluginClass.getDeclaredConstructor().newInstance() as Plugin
|
||||
loader.loadClass(manifest.pluginClassName) as Class<out BasePlugin?>
|
||||
val pluginInstance: BasePlugin =
|
||||
pluginClass.getDeclaredConstructor().newInstance() as BasePlugin
|
||||
|
||||
// Sets with the proper version
|
||||
setPluginData(data.copy(version = version))
|
||||
@ -575,7 +575,7 @@ object PluginManager {
|
||||
addAssetPath.invoke(assets, file.absolutePath)
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
pluginInstance.resources = Resources(
|
||||
(pluginInstance as? Plugin)?.resources = Resources(
|
||||
assets,
|
||||
context.resources.displayMetrics,
|
||||
context.resources.configuration
|
||||
@ -584,7 +584,11 @@ object PluginManager {
|
||||
plugins[filePath] = pluginInstance
|
||||
classLoaders[loader] = pluginInstance
|
||||
urlPlugins[data.url ?: filePath] = pluginInstance
|
||||
pluginInstance.load(context)
|
||||
if (pluginInstance is Plugin) {
|
||||
pluginInstance.load(context)
|
||||
} else {
|
||||
pluginInstance.load()
|
||||
}
|
||||
Log.i(TAG, "Loaded plugin ${data.internalName} successfully")
|
||||
currentlyLoading = null
|
||||
true
|
||||
|
@ -176,7 +176,8 @@ class PluginAdapter(
|
||||
if (data.isDownloaded) {
|
||||
// On local plugins page the filepath is provided instead of url.
|
||||
val plugin =
|
||||
PluginManager.urlPlugins[metadata.url] ?: PluginManager.plugins[metadata.url]
|
||||
(PluginManager.urlPlugins[metadata.url] ?: (PluginManager.plugins[metadata.url])) as? com.lagradost.cloudstream3.plugins.Plugin
|
||||
|
||||
if (plugin?.openSettings != null) {
|
||||
binding.actionSettings.isVisible = true
|
||||
binding.actionSettings.setOnClickListener {
|
||||
|
@ -102,7 +102,7 @@ class PluginDetailsFragment(val data: PluginViewData) : BottomSheetDialogFragmen
|
||||
if (data.isDownloaded) {
|
||||
// On local plugins page the filepath is provided instead of url.
|
||||
val plugin =
|
||||
PluginManager.urlPlugins[metadata.url] ?: PluginManager.plugins[metadata.url]
|
||||
(PluginManager.urlPlugins[metadata.url] ?: PluginManager.plugins[metadata.url]) as? com.lagradost.cloudstream3.plugins.Plugin
|
||||
if (plugin?.openSettings != null && context != null) {
|
||||
actionSettings.isVisible = true
|
||||
actionSettings.setOnClickListener {
|
||||
|
@ -0,0 +1,77 @@
|
||||
package com.lagradost.cloudstream3.plugins
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import com.lagradost.cloudstream3.APIHolder
|
||||
import com.lagradost.cloudstream3.MainAPI
|
||||
import com.lagradost.cloudstream3.utils.ExtractorApi
|
||||
import com.lagradost.api.Log
|
||||
import com.lagradost.cloudstream3.utils.extractorApis
|
||||
|
||||
const val PLUGIN_TAG = "PluginInstance"
|
||||
|
||||
abstract class BasePlugin {
|
||||
/**
|
||||
* 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
|
||||
// Race condition causing which would case duplicates if not for distinctBy
|
||||
synchronized(APIHolder.allProviders) {
|
||||
APIHolder.allProviders.add(element)
|
||||
}
|
||||
APIHolder.addPluginMapping(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)
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when your Plugin is being unloaded
|
||||
*/
|
||||
@Throws(Throwable::class)
|
||||
open fun beforeUnload() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when your Plugin is loaded
|
||||
*/
|
||||
@Throws(Throwable::class)
|
||||
open fun load() {
|
||||
}
|
||||
|
||||
/** Full file path to the plugin. */
|
||||
@Deprecated(
|
||||
"Renamed to `filename` to follow conventions",
|
||||
replaceWith = ReplaceWith("filename")
|
||||
)
|
||||
var __filename: String?
|
||||
get() = filename
|
||||
set(value) {
|
||||
filename = value
|
||||
}
|
||||
var filename: String? = null
|
||||
|
||||
|
||||
class Manifest {
|
||||
@JsonProperty("name")
|
||||
var name: String? = null
|
||||
|
||||
@JsonProperty("pluginClassName")
|
||||
var pluginClassName: String? = null
|
||||
|
||||
@JsonProperty("version")
|
||||
var version: Int? = null
|
||||
|
||||
@JsonProperty("requiresResources")
|
||||
var requiresResources: Boolean = false
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user