WIP updating block for subscriptions

This commit is contained in:
Kelvin 2024-11-12 14:37:57 +01:00
parent 965e74c7e2
commit dc9cc7b00f
2 changed files with 85 additions and 30 deletions

View file

@ -19,6 +19,7 @@ import com.futo.platformplayer.stores.PluginIconStorage
import com.futo.platformplayer.stores.PluginScriptsDirectory
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.serialization.Serializable
@ -47,6 +48,8 @@ class StatePlugins {
private var _updatesAvailableMap: HashSet<String> = hashSetOf();
private val _isUpdating: HashSet<String> = hashSetOf();
fun getPluginIconOrNull(id: String): ImageVariable? {
if(iconsDir.hasIcon(id))
return iconsDir.getIconBinary(id);
@ -58,6 +61,38 @@ class StatePlugins {
.load();
}
fun isUpdating(id: String): Boolean{
synchronized(_isUpdating){
return _isUpdating.contains(id);
}
}
fun setIsUpdating(id: String, value: Boolean){
synchronized(_isUpdating){
if(value && !_isUpdating.contains(id)) {
Logger.i(TAG, "PLUGIN [${id}] UPDATING");
_isUpdating.add(id);
}
if(!value && _isUpdating.contains(id)) {
Logger.i(TAG, "PLUGIN [${id}] NOT UPDATING");
_isUpdating.remove(id);
}
}
}
suspend fun whileUpdating(id: String, handle: suspend ()->Unit){
try {
setIsUpdating(id, true);
handle();
}
finally {
setIsUpdating(id, false);
}
}
fun clearUpdating(){
synchronized(_isUpdating) {
_isUpdating.clear();
}
}
suspend fun checkForUpdates(): List<Pair<SourcePluginConfig, SourcePluginConfig>> = withContext(Dispatchers.IO) {
var configs = mutableListOf<Pair<SourcePluginConfig, SourcePluginConfig>>()
@ -430,42 +465,49 @@ class StatePlugins {
fun installPluginBackground(context: Context, scope: CoroutineScope, config: SourcePluginConfig, script: String, onProgress: (text: String, progress: Double)->Unit, onConcluded: (ex: Throwable?)->Unit) {
scope.launch(Dispatchers.IO) {
val client = ManagedHttpClient();
try {
whileUpdating(config.id) {
withContext(Dispatchers.Main) {
onProgress.invoke("Validating script", 0.25);
onProgress.invoke("Waiting for plugins to finish", 0.1);
}
delay(500);
val tempDescriptor = SourcePluginDescriptor(config);
val plugin = JSClient(context, tempDescriptor, null, script);
plugin.validate();
withContext(Dispatchers.Main) {
onProgress.invoke("Downloading Icon", 0.5);
}
val icon = config.absoluteIconUrl?.let { absIconUrl ->
val client = ManagedHttpClient();
try {
withContext(Dispatchers.Main) {
onProgress.invoke("Saving plugin", 0.75);
onProgress.invoke("Validating script", 0.25);
}
val iconResp = client.get(absIconUrl);
if(iconResp.isOk)
return@let iconResp.body?.byteStream()?.use { it.readBytes() };
return@let null;
}
val installEx = StatePlugins.instance.createPlugin(config, script, icon, true);
if(installEx != null)
throw installEx;
StatePlatform.instance.updateAvailableClients(context);
withContext(Dispatchers.Main) {
onProgress.invoke("Finished", 1.0)
onConcluded.invoke(null);
}
} catch (ex: Exception) {
Logger.e(TAG, ex.message ?: "null", ex);
withContext(Dispatchers.Main) {
onConcluded.invoke(ex);
val tempDescriptor = SourcePluginDescriptor(config);
val plugin = JSClient(context, tempDescriptor, null, script);
plugin.validate();
withContext(Dispatchers.Main) {
onProgress.invoke("Downloading Icon", 0.5);
}
val icon = config.absoluteIconUrl?.let { absIconUrl ->
withContext(Dispatchers.Main) {
onProgress.invoke("Saving plugin", 0.75);
}
val iconResp = client.get(absIconUrl);
if (iconResp.isOk)
return@let iconResp.body?.byteStream()?.use { it.readBytes() };
return@let null;
}
val installEx = StatePlugins.instance.createPlugin(config, script, icon, true);
if (installEx != null)
throw installEx;
StatePlatform.instance.updateAvailableClients(context);
withContext(Dispatchers.Main) {
onProgress.invoke("Finished", 1.0)
onConcluded.invoke(null);
}
} catch (ex: Exception) {
Logger.e(TAG, ex.message ?: "null", ex);
withContext(Dispatchers.Main) {
onConcluded.invoke(ex);
}
}
}
}

View file

@ -22,6 +22,7 @@ import com.futo.platformplayer.models.Subscription
import com.futo.platformplayer.states.StateApp
import com.futo.platformplayer.states.StateCache
import com.futo.platformplayer.states.StatePlatform
import com.futo.platformplayer.states.StatePlugins
import com.futo.platformplayer.states.StateSubscriptions
import kotlinx.coroutines.CoroutineScope
import java.time.OffsetDateTime
@ -138,6 +139,18 @@ abstract class SubscriptionsTaskFetchAlgorithm(
for(task in tasks) {
val forkTask = threadPool.submit<SubscriptionTaskResult> {
if(StatePlugins.instance.isUpdating(task.client.id)){
val isUpdatingException = ScriptCriticalException(task.client.config, "Plugin is updating");
synchronized(failedPlugins) {
//Fail all subscription calls to plugin if it has a critical issue
if(isUpdatingException.config is SourcePluginConfig && !failedPlugins.contains(isUpdatingException.config.id)) {
Logger.w(StateSubscriptions.TAG, "Subscriptions ignoring plugin [${isUpdatingException.config.name}] due to critical exception:\n" + isUpdatingException.message);
failedPlugins.add(isUpdatingException.config.id);
}
}
return@submit SubscriptionTaskResult(task, StateCache.instance.getChannelCachePager(task.sub.channel.url), isUpdatingException);
}
if(task.fromPeek) {
try {