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.Context
|
||||||
import android.content.res.Resources
|
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 android.util.Log
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty
|
|
||||||
import com.lagradost.cloudstream3.actions.VideoClickAction
|
import com.lagradost.cloudstream3.actions.VideoClickAction
|
||||||
import com.lagradost.cloudstream3.actions.VideoClickActionHolder
|
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
|
* Called when your Plugin is loaded
|
||||||
* @param context Context
|
* @param context Context
|
||||||
*/
|
*/
|
||||||
@Throws(Throwable::class)
|
@Throws(Throwable::class)
|
||||||
open fun load(context: Context) {
|
open fun load(context: Context) {
|
||||||
}
|
// If not overridden by an extension then try the cross-platform load()
|
||||||
|
load()
|
||||||
/**
|
|
||||||
* 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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -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
|
* This will contain your resources if you specified requiresResources in gradle
|
||||||
*/
|
*/
|
||||||
var resources: Resources? = null
|
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
|
* This will add a button in the settings allowing you to add custom settings
|
||||||
|
@ -172,15 +172,15 @@ object PluginManager {
|
|||||||
var currentlyLoading: String? = null
|
var currentlyLoading: String? = null
|
||||||
|
|
||||||
// Maps filepath to plugin
|
// Maps filepath to plugin
|
||||||
val plugins: MutableMap<String, Plugin> =
|
val plugins: MutableMap<String, BasePlugin> =
|
||||||
LinkedHashMap<String, Plugin>()
|
LinkedHashMap<String, BasePlugin>()
|
||||||
|
|
||||||
// Maps urls to plugin
|
// Maps urls to plugin
|
||||||
val urlPlugins: MutableMap<String, Plugin> =
|
val urlPlugins: MutableMap<String, BasePlugin> =
|
||||||
LinkedHashMap<String, Plugin>()
|
LinkedHashMap<String, BasePlugin>()
|
||||||
|
|
||||||
private val classLoaders: MutableMap<PathClassLoader, Plugin> =
|
private val classLoaders: MutableMap<PathClassLoader, BasePlugin> =
|
||||||
HashMap<PathClassLoader, Plugin>()
|
HashMap<PathClassLoader, BasePlugin>()
|
||||||
|
|
||||||
var loadedLocalPlugins = false
|
var loadedLocalPlugins = false
|
||||||
private set
|
private set
|
||||||
@ -530,7 +530,7 @@ object PluginManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val loader = PathClassLoader(filePath, context.classLoader)
|
val loader = PathClassLoader(filePath, context.classLoader)
|
||||||
var manifest: Plugin.Manifest
|
var manifest: BasePlugin.Manifest
|
||||||
loader.getResourceAsStream("manifest.json").use { stream ->
|
loader.getResourceAsStream("manifest.json").use { stream ->
|
||||||
if (stream == null) {
|
if (stream == null) {
|
||||||
Log.e(TAG, "Failed to load plugin $fileName: No manifest found")
|
Log.e(TAG, "Failed to load plugin $fileName: No manifest found")
|
||||||
@ -539,7 +539,7 @@ object PluginManager {
|
|||||||
InputStreamReader(stream).use { reader ->
|
InputStreamReader(stream).use { reader ->
|
||||||
manifest = gson.fromJson(
|
manifest = gson.fromJson(
|
||||||
reader,
|
reader,
|
||||||
Plugin.Manifest::class.java
|
BasePlugin.Manifest::class.java
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -553,9 +553,9 @@ object PluginManager {
|
|||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
val pluginClass: Class<*> =
|
val pluginClass: Class<*> =
|
||||||
loader.loadClass(manifest.pluginClassName) as Class<out Plugin?>
|
loader.loadClass(manifest.pluginClassName) as Class<out BasePlugin?>
|
||||||
val pluginInstance: Plugin =
|
val pluginInstance: BasePlugin =
|
||||||
pluginClass.getDeclaredConstructor().newInstance() as Plugin
|
pluginClass.getDeclaredConstructor().newInstance() as BasePlugin
|
||||||
|
|
||||||
// Sets with the proper version
|
// Sets with the proper version
|
||||||
setPluginData(data.copy(version = version))
|
setPluginData(data.copy(version = version))
|
||||||
@ -575,7 +575,7 @@ object PluginManager {
|
|||||||
addAssetPath.invoke(assets, file.absolutePath)
|
addAssetPath.invoke(assets, file.absolutePath)
|
||||||
|
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
pluginInstance.resources = Resources(
|
(pluginInstance as? Plugin)?.resources = Resources(
|
||||||
assets,
|
assets,
|
||||||
context.resources.displayMetrics,
|
context.resources.displayMetrics,
|
||||||
context.resources.configuration
|
context.resources.configuration
|
||||||
@ -584,7 +584,11 @@ object PluginManager {
|
|||||||
plugins[filePath] = pluginInstance
|
plugins[filePath] = pluginInstance
|
||||||
classLoaders[loader] = pluginInstance
|
classLoaders[loader] = pluginInstance
|
||||||
urlPlugins[data.url ?: filePath] = 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")
|
Log.i(TAG, "Loaded plugin ${data.internalName} successfully")
|
||||||
currentlyLoading = null
|
currentlyLoading = null
|
||||||
true
|
true
|
||||||
|
@ -176,7 +176,8 @@ class PluginAdapter(
|
|||||||
if (data.isDownloaded) {
|
if (data.isDownloaded) {
|
||||||
// On local plugins page the filepath is provided instead of url.
|
// On local plugins page the filepath is provided instead of url.
|
||||||
val plugin =
|
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) {
|
if (plugin?.openSettings != null) {
|
||||||
binding.actionSettings.isVisible = true
|
binding.actionSettings.isVisible = true
|
||||||
binding.actionSettings.setOnClickListener {
|
binding.actionSettings.setOnClickListener {
|
||||||
|
@ -102,7 +102,7 @@ class PluginDetailsFragment(val data: PluginViewData) : BottomSheetDialogFragmen
|
|||||||
if (data.isDownloaded) {
|
if (data.isDownloaded) {
|
||||||
// On local plugins page the filepath is provided instead of url.
|
// On local plugins page the filepath is provided instead of url.
|
||||||
val plugin =
|
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) {
|
if (plugin?.openSettings != null && context != null) {
|
||||||
actionSettings.isVisible = true
|
actionSettings.isVisible = true
|
||||||
actionSettings.setOnClickListener {
|
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