forked from recloudstream/cloudstream
Added hot reloading for plugins when using deployWithAdb
This commit is contained in:
parent
7362ac9f64
commit
3fdf41869e
7 changed files with 89 additions and 35 deletions
|
@ -232,6 +232,9 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
|||
|
||||
/**
|
||||
* Fires every time a new batch of plugins have been loaded, no guarantee about how often this is run and on which thread
|
||||
* Boolean signifies if stuff should be force reloaded (true if force reload, false if reload when necessary).
|
||||
*
|
||||
* The force reloading are used for plugin development to instantly reload the page on deployWithAdb
|
||||
* */
|
||||
val afterPluginsLoadedEvent = Event<Boolean>()
|
||||
val mainPluginsLoadedEvent =
|
||||
|
@ -286,6 +289,11 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
|||
return true
|
||||
}
|
||||
}
|
||||
// This specific intent is used for the gradle deployWithAdb
|
||||
// https://github.com/recloudstream/gradle/blob/master/src/main/kotlin/com/lagradost/cloudstream3/gradle/tasks/DeployWithAdbTask.kt#L46
|
||||
if (str == "$appString:") {
|
||||
PluginManager.hotReloadAllLocalPlugins(activity)
|
||||
}
|
||||
} else if (safeURI(str)?.scheme == appStringRepo) {
|
||||
val url = str.replaceFirst(appStringRepo, "https")
|
||||
loadRepository(url)
|
||||
|
@ -650,7 +658,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
|||
}
|
||||
|
||||
ioSafe {
|
||||
PluginManager.loadAllLocalPlugins(this@MainActivity)
|
||||
PluginManager.loadAllLocalPlugins(this@MainActivity, false)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -10,6 +10,7 @@ import android.util.Log
|
|||
import android.widget.Toast
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import com.google.gson.Gson
|
||||
import com.lagradost.cloudstream3.*
|
||||
|
@ -223,7 +224,7 @@ object PluginManager {
|
|||
fun updateAllOnlinePluginsAndLoadThem(activity: Activity) {
|
||||
// Load all plugins as fast as possible!
|
||||
loadAllOnlinePlugins(activity)
|
||||
afterPluginsLoadedEvent.invoke(true)
|
||||
afterPluginsLoadedEvent.invoke(false)
|
||||
|
||||
val urls = (getKey<Array<RepositoryData>>(REPOSITORIES_KEY)
|
||||
?: emptyArray()) + PREBUILT_REPOSITORIES
|
||||
|
@ -272,7 +273,7 @@ object PluginManager {
|
|||
}
|
||||
|
||||
// ioSafe {
|
||||
afterPluginsLoadedEvent.invoke(true)
|
||||
afterPluginsLoadedEvent.invoke(false)
|
||||
// }
|
||||
|
||||
Log.i(TAG, "Plugin update done!")
|
||||
|
@ -299,8 +300,12 @@ object PluginManager {
|
|||
val notDownloadedPlugins = onlinePlugins.mapNotNull { onlineData ->
|
||||
val sitePlugin = onlineData.second
|
||||
//Don't include empty urls
|
||||
if (sitePlugin.url.isBlank()) { return@mapNotNull null }
|
||||
if (sitePlugin.repositoryUrl.isNullOrBlank()) { return@mapNotNull null }
|
||||
if (sitePlugin.url.isBlank()) {
|
||||
return@mapNotNull null
|
||||
}
|
||||
if (sitePlugin.repositoryUrl.isNullOrBlank()) {
|
||||
return@mapNotNull null
|
||||
}
|
||||
|
||||
//Omit already existing plugins
|
||||
if (getPluginPath(activity, sitePlugin.internalName, onlineData.first).exists()) {
|
||||
|
@ -353,7 +358,7 @@ object PluginManager {
|
|||
}
|
||||
|
||||
// ioSafe {
|
||||
afterPluginsLoadedEvent.invoke(true)
|
||||
afterPluginsLoadedEvent.invoke(false)
|
||||
// }
|
||||
|
||||
Log.i(TAG, "Plugin download done!")
|
||||
|
@ -373,7 +378,23 @@ object PluginManager {
|
|||
}
|
||||
}
|
||||
|
||||
fun loadAllLocalPlugins(activity: Activity) {
|
||||
/**
|
||||
* Reloads all local plugins and forces a page update, used for hot reloading with deployWithAdb
|
||||
**/
|
||||
fun hotReloadAllLocalPlugins(activity: FragmentActivity?) {
|
||||
Log.d(TAG, "Reloading all local plugins!")
|
||||
if (activity == null) return
|
||||
getPluginsLocal().forEach {
|
||||
unloadPlugin(it.filePath)
|
||||
}
|
||||
loadAllLocalPlugins(activity, true)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param forceReload see afterPluginsLoadedEvent, basically a way to load all local plugins
|
||||
* and reload all pages even if they are previously valid
|
||||
**/
|
||||
fun loadAllLocalPlugins(activity: Activity, forceReload: Boolean) {
|
||||
val dir = File(LOCAL_PLUGINS_PATH)
|
||||
removeKey(PLUGINS_KEY_LOCAL)
|
||||
|
||||
|
@ -395,7 +416,7 @@ object PluginManager {
|
|||
}
|
||||
|
||||
loadedLocalPlugins = true
|
||||
afterPluginsLoadedEvent.invoke(true)
|
||||
afterPluginsLoadedEvent.invoke(forceReload)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -576,7 +597,8 @@ object PluginManager {
|
|||
}
|
||||
|
||||
suspend fun deletePlugin(file: File): Boolean {
|
||||
val list = (getPluginsLocal() + getPluginsOnline()).filter { it.filePath == file.absolutePath }
|
||||
val list =
|
||||
(getPluginsLocal() + getPluginsOnline()).filter { it.filePath == file.absolutePath }
|
||||
|
||||
return try {
|
||||
if (File(file.absolutePath).delete()) {
|
||||
|
|
|
@ -3,9 +3,11 @@ package com.lagradost.cloudstream3.ui
|
|||
import com.lagradost.cloudstream3.*
|
||||
import com.lagradost.cloudstream3.APIHolder.unixTime
|
||||
import com.lagradost.cloudstream3.APIHolder.unixTimeMS
|
||||
import com.lagradost.cloudstream3.MainActivity.Companion.afterPluginsLoadedEvent
|
||||
import com.lagradost.cloudstream3.mvvm.Resource
|
||||
import com.lagradost.cloudstream3.mvvm.logError
|
||||
import com.lagradost.cloudstream3.mvvm.safeApiCall
|
||||
import com.lagradost.cloudstream3.utils.Coroutines.threadSafeListOf
|
||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
|
@ -38,13 +40,23 @@ class APIRepository(val api: MainAPI) {
|
|||
val hash: Pair<String, String>
|
||||
)
|
||||
|
||||
// This really does not need to be Nullable but it can crash otherwise, probably caused by replacing items while looping over them.
|
||||
// "Attempt to invoke .... getHash() on a null object reference"
|
||||
private val cache: ArrayList<SavedLoadResponse?> = arrayListOf()
|
||||
private val cache = threadSafeListOf<SavedLoadResponse>()
|
||||
private var cacheIndex: Int = 0
|
||||
const val cacheSize = 20
|
||||
}
|
||||
|
||||
private fun afterPluginsLoaded(forceReload: Boolean) {
|
||||
if (forceReload) {
|
||||
synchronized(cache) {
|
||||
cache.clear()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
afterPluginsLoadedEvent += ::afterPluginsLoaded
|
||||
}
|
||||
|
||||
val hasMainPage = api.hasMainPage
|
||||
val providerType = api.providerType
|
||||
val name = api.name
|
||||
|
@ -59,21 +71,26 @@ class APIRepository(val api: MainAPI) {
|
|||
val fixedUrl = api.fixUrl(url)
|
||||
val lookingForHash = Pair(api.name, fixedUrl)
|
||||
|
||||
synchronized(cache) {
|
||||
for (item in cache) {
|
||||
// 10 min save
|
||||
if (item?.hash == lookingForHash && (unixTime - item.unixTime) < 60 * 10) {
|
||||
if (item.hash == lookingForHash && (unixTime - item.unixTime) < 60 * 10) {
|
||||
return@safeApiCall item.response
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
api.load(fixedUrl)?.also { response ->
|
||||
val add = SavedLoadResponse(unixTime, response, lookingForHash)
|
||||
|
||||
synchronized(cache) {
|
||||
if (cache.size > cacheSize) {
|
||||
cache[cacheIndex] = add // rolling cache
|
||||
cacheIndex = (cacheIndex + 1) % cacheSize
|
||||
} else {
|
||||
cache.add(add)
|
||||
}
|
||||
}
|
||||
} ?: throw ErrorLoadingException()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -79,7 +79,6 @@ import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar
|
|||
import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbarView
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.getResourceColor
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.getSpanCount
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.getStatusBarHeight
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.popupMenuNoIconsAndNoStringRes
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.setImage
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.setImageBlur
|
||||
|
@ -475,13 +474,13 @@ class HomeFragment : Fragment() {
|
|||
override fun onResume() {
|
||||
super.onResume()
|
||||
reloadStored()
|
||||
afterPluginsLoadedEvent += ::firstLoadHomePage
|
||||
mainPluginsLoadedEvent += ::firstLoadHomePage
|
||||
afterPluginsLoadedEvent += ::afterPluginsLoaded
|
||||
mainPluginsLoadedEvent += ::afterMainPluginsLoaded
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
afterPluginsLoadedEvent -= ::firstLoadHomePage
|
||||
mainPluginsLoadedEvent -= ::firstLoadHomePage
|
||||
afterPluginsLoadedEvent -= ::afterPluginsLoaded
|
||||
mainPluginsLoadedEvent -= ::afterMainPluginsLoaded
|
||||
super.onStop()
|
||||
}
|
||||
|
||||
|
@ -494,15 +493,18 @@ class HomeFragment : Fragment() {
|
|||
homeViewModel.loadStoredData(list)
|
||||
}
|
||||
|
||||
private fun firstLoadHomePage(successful: Boolean = false) {
|
||||
// dirty hack to make it only load once
|
||||
private fun afterMainPluginsLoaded(unused: Boolean = false) {
|
||||
loadHomePage(false)
|
||||
}
|
||||
|
||||
private fun loadHomePage(forceReload: Boolean = true) {
|
||||
private fun afterPluginsLoaded(forceReload: Boolean) {
|
||||
loadHomePage(forceReload)
|
||||
}
|
||||
|
||||
private fun loadHomePage(forceReload: Boolean) {
|
||||
val apiName = context?.getKey<String>(USER_SELECTED_HOMEPAGE_API)
|
||||
|
||||
if (homeViewModel.apiName.value != apiName || apiName == null) {
|
||||
if (homeViewModel.apiName.value != apiName || apiName == null || forceReload) {
|
||||
//println("Caught home: " + homeViewModel.apiName.value + " at " + apiName)
|
||||
homeViewModel.loadAndCancel(apiName, forceReload)
|
||||
}
|
||||
|
@ -1120,7 +1122,7 @@ class HomeFragment : Fragment() {
|
|||
} // GridLayoutManager(context, 1).also { it.supportsPredictiveItemAnimations() }
|
||||
|
||||
reloadStored()
|
||||
loadHomePage()
|
||||
loadHomePage(false)
|
||||
|
||||
home_loaded.setOnScrollChangeListener(NestedScrollView.OnScrollChangeListener { v, _, scrollY, _, oldScrollY ->
|
||||
val dy = scrollY - oldScrollY
|
||||
|
|
|
@ -58,6 +58,7 @@ const val ACTION_DOWNLOAD_EPISODE_SUBTITLE_MIRROR = 14
|
|||
const val ACTION_PLAY_EPISODE_IN_WEB_VIDEO = 16
|
||||
const val ACTION_PLAY_EPISODE_IN_MPV = 17
|
||||
|
||||
const val ACTION_MARK_AS_WATCHED = 18
|
||||
|
||||
data class EpisodeClickEvent(val action: Int, val data: ResultEpisode)
|
||||
|
||||
|
|
|
@ -491,11 +491,10 @@ open class ResultFragment : ResultTrailerPlayer() {
|
|||
return StoredData(url, apiName, showFillers, dubStatus, start, playerAction)
|
||||
}
|
||||
|
||||
private fun reloadViewModel(success: Boolean = false) {
|
||||
if (!viewModel.hasLoaded()) {
|
||||
private fun reloadViewModel(forceReload: Boolean) {
|
||||
if (!viewModel.hasLoaded() || forceReload) {
|
||||
val storedData = getStoredData(activity ?: context ?: return) ?: return
|
||||
|
||||
//viewModel.clear()
|
||||
viewModel.load(
|
||||
activity,
|
||||
storedData.url ?: return,
|
||||
|
|
|
@ -1146,6 +1146,7 @@ class ResultViewModel2 : ViewModel() {
|
|||
txt(R.string.episode_action_download_mirror) to ACTION_DOWNLOAD_MIRROR,
|
||||
txt(R.string.episode_action_download_subtitle) to ACTION_DOWNLOAD_EPISODE_SUBTITLE_MIRROR,
|
||||
txt(R.string.episode_action_reload_links) to ACTION_RELOAD_EPISODE,
|
||||
// txt(R.string.action_mark_as_watched) to ACTION_MARK_AS_WATCHED,
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -1365,7 +1366,7 @@ class ResultViewModel2 : ViewModel() {
|
|||
R.id.global_to_navigation_player,
|
||||
GeneratorPlayer.newInstance(
|
||||
generator?.also {
|
||||
it.getAll() // I know kinda shit to itterate all, but it is 100% sure to work
|
||||
it.getAll() // I know kinda shit to iterate all, but it is 100% sure to work
|
||||
?.indexOfFirst { value -> value is ResultEpisode && value.id == click.data.id }
|
||||
?.let { index ->
|
||||
if (index >= 0)
|
||||
|
@ -1376,6 +1377,10 @@ class ResultViewModel2 : ViewModel() {
|
|||
)
|
||||
)
|
||||
}
|
||||
ACTION_MARK_AS_WATCHED -> {
|
||||
// TODO FIX
|
||||
// DataStoreHelper.setViewPos(click.data.id, 1, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue