diff --git a/app/build.gradle b/app/build.gradle index fcbd422c..8c30e58d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -179,7 +179,8 @@ dependencies { implementation 'com.google.code.gson:gson:2.10.1' //Used for complex/anonymous cases like during development conversions (eg. V8RemoteObject) //JS - implementation("com.caoccao.javet:javet-android:3.0.2") + //implementation("com.caoccao.javet:javet-android:3.0.2") + implementation 'com.caoccao.javet:javet-v8-android:4.1.4' //Exoplayer implementation 'androidx.media3:media3-exoplayer:1.2.1' diff --git a/app/src/main/assets/scripts/source.js b/app/src/main/assets/scripts/source.js index 0638f079..9f38404d 100644 --- a/app/src/main/assets/scripts/source.js +++ b/app/src/main/assets/scripts/source.js @@ -103,6 +103,12 @@ class UnavailableException extends ScriptException { super("UnavailableException", msg); } } +class ReloadRequiredException extends ScriptException { + constructor(msg, reloadData) { + super("ReloadRequiredException", msg); + this.reloadData = reloadData; + } +} class AgeException extends ScriptException { constructor(msg) { super("AgeException", msg); diff --git a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/JSClient.kt b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/JSClient.kt index 476bad8a..6ec1eae2 100644 --- a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/JSClient.kt +++ b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/JSClient.kt @@ -62,6 +62,7 @@ import com.futo.platformplayer.states.StatePlugins import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json import java.time.OffsetDateTime +import java.util.Random import kotlin.Exception import kotlin.reflect.full.findAnnotations import kotlin.reflect.jvm.kotlinFunction @@ -106,6 +107,8 @@ open class JSClient : IPlatformClient { return _busyAction; } + val declareOnEnable = HashMap(); + val settings: HashMap get() = descriptor.settings; val flags: Array; @@ -213,6 +216,10 @@ open class JSClient : IPlatformClient { return plugin.httpClientOthers[id]; } + fun setReloadData(data: String?) { + declareOnEnable.put("__reloadData", data ?: ""); + } + override fun initialize() { if (_initialized) return @@ -263,7 +270,13 @@ open class JSClient : IPlatformClient { fun enable() { if(!_initialized) initialize(); + for(toDeclare in declareOnEnable) { + plugin.execute("var ${toDeclare.key} = " + Json.encodeToString(toDeclare.value)); + } plugin.execute("source.enable(${Json.encodeToString(config)}, parseSettings(${Json.encodeToString(descriptor.getSettingsWithDefaults())}), ${Json.encodeToString(_injectedSaveState)})"); + + if(declareOnEnable.containsKey("__reloadData")) + declareOnEnable.remove("__reloadData"); _enabled = true; } @JSDocs(1, "source.saveState()", "Provide a string that is passed to enable for quicker startup of multiple instances") @@ -735,8 +748,12 @@ open class JSClient : IPlatformClient { } - private fun isBusyWith(actionName: String, handle: ()->T): T { + + fun isBusyWith(actionName: String, handle: ()->T): T { + val busyId = kotlin.random.Random.nextInt(9999); try { + + Logger.v(TAG, "Busy with [${actionName}] (${busyId})") synchronized(_busyLock) { _busyCounter++; } @@ -748,6 +765,7 @@ open class JSClient : IPlatformClient { synchronized(_busyLock) { _busyCounter--; } + Logger.v(TAG, "Busy done [${actionName}] (${busyId})") } } private fun isBusyWith(handle: ()->T): T { diff --git a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSDashManifestRawAudioSource.kt b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSDashManifestRawAudioSource.kt index ae35207b..2c6d4b35 100644 --- a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSDashManifestRawAudioSource.kt +++ b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSDashManifestRawAudioSource.kt @@ -62,12 +62,16 @@ class JSDashManifestRawAudioSource : JSSource, IAudioSource, IJSDashManifestRawS if(_plugin is DevJSClient) result = StateDeveloper.instance.handleDevCall(_plugin.devID, "DashManifestRaw", false) { _plugin.getUnderlyingPlugin().catchScriptErrors("DashManifestRaw", "dashManifestRaw.generate()") { - _obj.invokeString("generate"); + _plugin.isBusyWith("dashAudio.generate") { + _obj.invokeString("generate"); + } } } else result = _plugin.getUnderlyingPlugin().catchScriptErrors("DashManifestRaw", "dashManifestRaw.generate()") { - _obj.invokeString("generate"); + _plugin.isBusyWith("dashAudio.generate") { + _obj.invokeString("generate"); + } } if(result != null){ diff --git a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSDashManifestRawSource.kt b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSDashManifestRawSource.kt index d6ff7455..a9c070f7 100644 --- a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSDashManifestRawSource.kt +++ b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSDashManifestRawSource.kt @@ -67,13 +67,17 @@ open class JSDashManifestRawSource: JSSource, IVideoSource, IJSDashManifestRawSo if(_plugin is DevJSClient) { result = StateDeveloper.instance.handleDevCall(_plugin.devID, "DashManifestRawSource.generate()") { _plugin.getUnderlyingPlugin().catchScriptErrors("DashManifestRaw.generate", "generate()", { - _obj.invokeString("generate"); + _plugin.isBusyWith("dashVideo.generate") { + _obj.invokeString("generate"); + } }); } } else result = _plugin.getUnderlyingPlugin().catchScriptErrors("DashManifestRaw.generate", "generate()", { - _obj.invokeString("generate"); + _plugin.isBusyWith("dashVideo.generate") { + _obj.invokeString("generate"); + } }); if(result != null){ diff --git a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSSource.kt b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSSource.kt index 3c76e23d..ee586083 100644 --- a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSSource.kt +++ b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSSource.kt @@ -75,9 +75,11 @@ abstract class JSSource { if (!hasRequestExecutor || _obj.isClosed) return null; + Logger.v("JSSource", "Request executor for [${type}] requesting"); val result = V8Plugin.catchScriptErrors(_config, "[${_config.name}] JSSource", "obj.getRequestExecutor()") { _obj.invoke("getRequestExecutor", arrayOf()); }; + Logger.v("JSSource", "Request executor for [${type}] received"); if (result !is V8ValueObject) return null; diff --git a/app/src/main/java/com/futo/platformplayer/engine/V8Plugin.kt b/app/src/main/java/com/futo/platformplayer/engine/V8Plugin.kt index 15412fd9..96593f48 100644 --- a/app/src/main/java/com/futo/platformplayer/engine/V8Plugin.kt +++ b/app/src/main/java/com/futo/platformplayer/engine/V8Plugin.kt @@ -15,6 +15,7 @@ import com.caoccao.javet.values.primitive.V8ValueString import com.caoccao.javet.values.reference.V8ValueObject import com.futo.platformplayer.api.http.ManagedHttpClient import com.futo.platformplayer.api.media.platforms.js.internal.JSHttpClient +import com.futo.platformplayer.assume import com.futo.platformplayer.constructs.Event1 import com.futo.platformplayer.engine.exceptions.NoInternetException import com.futo.platformplayer.engine.exceptions.PluginEngineStoppedException @@ -26,6 +27,7 @@ import com.futo.platformplayer.engine.exceptions.ScriptException import com.futo.platformplayer.engine.exceptions.ScriptExecutionException import com.futo.platformplayer.engine.exceptions.ScriptImplementationException import com.futo.platformplayer.engine.exceptions.ScriptLoginRequiredException +import com.futo.platformplayer.engine.exceptions.ScriptReloadRequiredException import com.futo.platformplayer.engine.exceptions.ScriptTimeoutException import com.futo.platformplayer.engine.exceptions.ScriptUnavailableException import com.futo.platformplayer.engine.internal.V8Converter @@ -186,6 +188,7 @@ class V8Plugin { Logger.i(TAG, "Stopping plugin [${config.name}]"); isStopped = true; whenNotBusy { + Logger.i(TAG, "Plugin stopping"); synchronized(_runtimeLock) { isStopped = true; @@ -200,7 +203,7 @@ class V8Plugin { _runtime = null; if(!it.isClosed && !it.isDead) { try { - it.close(); + it.close(true); } catch(ex: JavetException) { //In case race conditions are going on, already closed runtimes are fine. @@ -211,6 +214,7 @@ class V8Plugin { Logger.i(TAG, "Stopped plugin [${config.name}]"); }; } + Logger.i(TAG, "Plugin stopped"); onStopped.emit(this); } } @@ -327,26 +331,38 @@ class V8Plugin { throw ScriptCompilationException(config, "Compilation: [${context}]: ${scriptEx.message}\n(${scriptEx.scriptingError.lineNumber})[${scriptEx.scriptingError.startColumn}-${scriptEx.scriptingError.endColumn}]: ${scriptEx.scriptingError.sourceLine}", null, codeStripped); } catch(executeEx: JavetExecutionException) { - if(executeEx.scriptingError?.context?.containsKey("plugin_type") == true) { - val pluginType = executeEx.scriptingError.context["plugin_type"].toString(); + if(executeEx.scriptingError?.context is V8ValueObject) { + val obj = executeEx.scriptingError?.context as V8ValueObject + if(obj.has("plugin_type") == true) { + val pluginType = obj.get("plugin_type").toString(); - //Captcha - if (pluginType == "CaptchaRequiredException") { - throw ScriptCaptchaRequiredException(config, - executeEx.scriptingError.context["url"]?.toString(), - executeEx.scriptingError.context["body"]?.toString(), - executeEx, executeEx.scriptingError?.stack, codeStripped); + //Captcha + if (pluginType == "CaptchaRequiredException") { + throw ScriptCaptchaRequiredException(config, + obj.get("url")?.toString(), + obj.get("body")?.toString(), + executeEx, executeEx.scriptingError?.stack, codeStripped); + } + + //Reload Required + if (pluginType == "ReloadRequiredException") { + throw ScriptReloadRequiredException(config, + obj.get("message")?.toString(), + obj.get("reloadData")?.toString(), + executeEx, executeEx.scriptingError?.stack, codeStripped); + } + + //Others + throwExceptionFromV8( + config, + pluginType, + (extractJSExceptionMessage(executeEx) ?: ""), + executeEx, + executeEx.scriptingError?.stack, + codeStripped + ); } - //Others - throwExceptionFromV8( - config, - pluginType, - (extractJSExceptionMessage(executeEx) ?: ""), - executeEx, - executeEx.scriptingError?.stack, - codeStripped - ); } throw ScriptExecutionException(config, extractJSExceptionMessage(executeEx) ?: "", null, executeEx.scriptingError?.stack, codeStripped); } diff --git a/app/src/main/java/com/futo/platformplayer/engine/exceptions/ScriptReloadRequiredException.kt b/app/src/main/java/com/futo/platformplayer/engine/exceptions/ScriptReloadRequiredException.kt new file mode 100644 index 00000000..98c0635d --- /dev/null +++ b/app/src/main/java/com/futo/platformplayer/engine/exceptions/ScriptReloadRequiredException.kt @@ -0,0 +1,20 @@ +package com.futo.platformplayer.engine.exceptions + +import com.caoccao.javet.values.reference.V8ValueObject +import com.futo.platformplayer.api.media.platforms.js.SourcePluginConfig +import com.futo.platformplayer.engine.IV8PluginConfig +import com.futo.platformplayer.engine.V8PluginConfig +import com.futo.platformplayer.getOrDefault +import com.futo.platformplayer.getOrThrow + +class ScriptReloadRequiredException(config: IV8PluginConfig, val msg: String?, val reloadData: String?, ex: Exception? = null, stack: String? = null, code: String? = null) : ScriptException(config, msg ?: "ReloadRequired", ex, stack, code) { + + companion object { + fun fromV8(config: IV8PluginConfig, obj: V8ValueObject) : ScriptException { + val contextName = "ScriptReloadRequiredException"; + return ScriptReloadRequiredException(config, + obj.getOrThrow(config, "message", contextName), + obj.getOrDefault(config, "reloadData", contextName, null)); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/futo/platformplayer/engine/packages/PackageBridge.kt b/app/src/main/java/com/futo/platformplayer/engine/packages/PackageBridge.kt index a5f18705..5450c5eb 100644 --- a/app/src/main/java/com/futo/platformplayer/engine/packages/PackageBridge.kt +++ b/app/src/main/java/com/futo/platformplayer/engine/packages/PackageBridge.kt @@ -78,6 +78,13 @@ class PackageBridge : V8Package { return "android"; } + @V8Property + fun supportedFeatures(): Array { + return arrayOf( + "ReloadRequiredException" + ); + } + @V8Property fun supportedContent(): Array { return arrayOf( diff --git a/app/src/main/java/com/futo/platformplayer/engine/packages/PackageHttp.kt b/app/src/main/java/com/futo/platformplayer/engine/packages/PackageHttp.kt index aa78a184..0b049ecb 100644 --- a/app/src/main/java/com/futo/platformplayer/engine/packages/PackageHttp.kt +++ b/app/src/main/java/com/futo/platformplayer/engine/packages/PackageHttp.kt @@ -44,6 +44,17 @@ class PackageHttp: V8Package { private val aliveSockets = mutableListOf(); private var _cleanedUp = false; + private val _clients = mutableMapOf() + + fun getClient(id: String?): PackageHttpClient { + if(id == null) + throw IllegalArgumentException("Http client ${id} doesn't exist"); + if(_packageClient.clientId() == id) + return _packageClient; + if(_packageClientAuth.clientId() == id) + return _packageClientAuth; + return _clients.getOrDefault(id, null) ?: throw IllegalArgumentException("Http client ${id} doesn't exist"); + } constructor(plugin: V8Plugin, config: IV8PluginConfig): super(plugin) { _config = config; @@ -112,6 +123,8 @@ class PackageHttp: V8Package { _plugin.registerHttpClient(httpClient); val client = PackageHttpClient(this, httpClient); + _clients.put(client.clientId() ?: "", client); + return client; } @V8Function @@ -246,18 +259,18 @@ class PackageHttp: V8Package { @V8Function fun request(method: String, url: String, headers: MutableMap = HashMap(), useAuth: Boolean = false) : BatchBuilder { - return clientRequest(_package.getDefaultClient(useAuth), method, url, headers); + return clientRequest(_package.getDefaultClient(useAuth).clientId(), method, url, headers); } @V8Function fun requestWithBody(method: String, url: String, body:String, headers: MutableMap = HashMap(), useAuth: Boolean = false) : BatchBuilder { - return clientRequestWithBody(_package.getDefaultClient(useAuth), method, url, body, headers); + return clientRequestWithBody(_package.getDefaultClient(useAuth).clientId(), method, url, body, headers); } @V8Function fun GET(url: String, headers: MutableMap = HashMap(), useAuth: Boolean = false) : BatchBuilder - = clientGET(_package.getDefaultClient(useAuth), url, headers); + = clientGET(_package.getDefaultClient(useAuth).clientId(), url, headers); @V8Function fun POST(url: String, body: String, headers: MutableMap = HashMap(), useAuth: Boolean = false) : BatchBuilder - = clientPOST(_package.getDefaultClient(useAuth), url, body, headers); + = clientPOST(_package.getDefaultClient(useAuth).clientId(), url, body, headers); @V8Function fun DUMMY(): BatchBuilder { @@ -268,21 +281,21 @@ class PackageHttp: V8Package { //Client-specific @V8Function - fun clientRequest(client: PackageHttpClient, method: String, url: String, headers: MutableMap = HashMap()) : BatchBuilder { - _reqs.add(Pair(client, RequestDescriptor(method, url, headers))); + fun clientRequest(clientId: String?, method: String, url: String, headers: MutableMap = HashMap()) : BatchBuilder { + _reqs.add(Pair(_package.getClient(clientId), RequestDescriptor(method, url, headers))); return BatchBuilder(_package, _reqs); } @V8Function - fun clientRequestWithBody(client: PackageHttpClient, method: String, url: String, body:String, headers: MutableMap = HashMap()) : BatchBuilder { - _reqs.add(Pair(client, RequestDescriptor(method, url, headers, body))); + fun clientRequestWithBody(clientId: String?, method: String, url: String, body:String, headers: MutableMap = HashMap()) : BatchBuilder { + _reqs.add(Pair(_package.getClient(clientId), RequestDescriptor(method, url, headers, body))); return BatchBuilder(_package, _reqs); } @V8Function - fun clientGET(client: PackageHttpClient, url: String, headers: MutableMap = HashMap()) : BatchBuilder - = clientRequest(client, "GET", url, headers); + fun clientGET(clientId: String?, url: String, headers: MutableMap = HashMap()) : BatchBuilder + = clientRequest(clientId, "GET", url, headers); @V8Function - fun clientPOST(client: PackageHttpClient, url: String, body: String, headers: MutableMap = HashMap()) : BatchBuilder - = clientRequestWithBody(client, "POST", url, body, headers); + fun clientPOST(clientId: String?, url: String, body: String, headers: MutableMap = HashMap()) : BatchBuilder + = clientRequestWithBody(clientId, "POST", url, body, headers); //Finalizer @@ -321,6 +334,7 @@ class PackageHttp: V8Package { @Transient private val _clientId: String?; + @V8Property fun clientId(): String? { return _clientId; diff --git a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailView.kt b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailView.kt index a6241c29..08e04730 100644 --- a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailView.kt +++ b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailView.kt @@ -93,6 +93,7 @@ import com.futo.platformplayer.engine.exceptions.ScriptAgeException import com.futo.platformplayer.engine.exceptions.ScriptException import com.futo.platformplayer.engine.exceptions.ScriptImplementationException import com.futo.platformplayer.engine.exceptions.ScriptLoginRequiredException +import com.futo.platformplayer.engine.exceptions.ScriptReloadRequiredException import com.futo.platformplayer.engine.exceptions.ScriptUnavailableException import com.futo.platformplayer.exceptions.UnsupportedCastException import com.futo.platformplayer.fixHtmlLinks @@ -608,6 +609,10 @@ class VideoDetailView : ConstraintLayout { } } + _player.onReloadRequired.subscribe { + fetchVideo(); + } + _player.onPlayChanged.subscribe { if (StateCasting.instance.activeDevice == null) { handlePlayChanged(it); @@ -3025,6 +3030,11 @@ class VideoDetailView : ConstraintLayout { return@TaskHandler result; }) .success { setVideoDetails(it, true) } + .exception { + StatePlatform.instance.handleReloadRequired(it, { + fetchVideo(); + }); + } .exception { Logger.w(TAG, "exception", it) diff --git a/app/src/main/java/com/futo/platformplayer/states/StatePlatform.kt b/app/src/main/java/com/futo/platformplayer/states/StatePlatform.kt index c843ea9f..8cf8f080 100644 --- a/app/src/main/java/com/futo/platformplayer/states/StatePlatform.kt +++ b/app/src/main/java/com/futo/platformplayer/states/StatePlatform.kt @@ -2,6 +2,7 @@ package com.futo.platformplayer.states import android.content.Context import androidx.collection.LruCache +import androidx.lifecycle.lifecycleScope import com.futo.platformplayer.R import com.futo.platformplayer.Settings import com.futo.platformplayer.UIDialogs @@ -38,6 +39,7 @@ import com.futo.platformplayer.awaitFirstNotNullDeferred import com.futo.platformplayer.constructs.BatchedTaskHandler import com.futo.platformplayer.constructs.Event0 import com.futo.platformplayer.constructs.Event1 +import com.futo.platformplayer.engine.exceptions.ScriptReloadRequiredException import com.futo.platformplayer.fromPool import com.futo.platformplayer.getNowDiffDays import com.futo.platformplayer.getNowDiffSeconds @@ -316,7 +318,18 @@ class StatePlatform { _platformOrderPersistent.save(); } - suspend fun reloadClient(context: Context, id: String) : JSClient? { + fun handleReloadRequired(reloadRequiredException: ScriptReloadRequiredException, afterReload: (() -> Unit)? = null) { + val id = if(reloadRequiredException.config is SourcePluginConfig) reloadRequiredException.config.id else ""; + UIDialogs.appToast("Reloading [${reloadRequiredException.config.name}] by plugin request"); + StateApp.instance.scopeOrNull?.launch(Dispatchers.IO) { + if(!reloadRequiredException.reloadData.isNullOrEmpty()) + reEnableClientWithData(id, reloadRequiredException.reloadData, afterReload); + else + reEnableClient(id, afterReload); + } + } + + suspend fun reloadClient(context: Context, id: String, afterReload: (()->Unit)? = null) : JSClient? { return withContext(Dispatchers.IO) { val client = getClient(id); if (client !is JSClient) @@ -347,10 +360,27 @@ class StatePlatform { _availableClients.removeIf { it.id == id }; _availableClients.add(newClient); } + afterReload?.invoke(); return@withContext newClient; }; } + suspend fun reEnableClientWithData(id: String, data: String? = null, afterReload: (()->Unit)? = null) { + val enabledBefore = getEnabledClients().map { it.id }; + if(data != null) { + val client = getClientOrNull(id); + if(client != null && client is JSClient) + client.setReloadData(data); + } + selectClients({ + _scope.launch(Dispatchers.IO) { + selectClients({ + afterReload?.invoke(); + }, *(enabledBefore).distinct().toTypedArray()); + } + }, *(enabledBefore.filter { it != id }).distinct().toTypedArray()) + } + suspend fun reEnableClient(id: String, afterReload: (()->Unit)? = null) = reEnableClientWithData(id, null, afterReload); suspend fun enableClient(ids: List) { val currentClients = getEnabledClients().map { it.id }; @@ -361,6 +391,9 @@ class StatePlatform { * If a client is disabled, NO requests are made to said client */ suspend fun selectClients(vararg ids: String) { + selectClients(null, *ids); + } + suspend fun selectClients(afterLoad: (() -> Unit)?, vararg ids: String) { withContext(Dispatchers.IO) { synchronized(_clientsLock) { val removed = _enabledClients.toMutableList(); @@ -385,6 +418,7 @@ class StatePlatform { onSourceDisabled.emit(oldClient); } } + afterLoad?.invoke(); }; } diff --git a/app/src/main/java/com/futo/platformplayer/views/video/FutoVideoPlayerBase.kt b/app/src/main/java/com/futo/platformplayer/views/video/FutoVideoPlayerBase.kt index 60c5dbf2..ab0bf383 100644 --- a/app/src/main/java/com/futo/platformplayer/views/video/FutoVideoPlayerBase.kt +++ b/app/src/main/java/com/futo/platformplayer/views/video/FutoVideoPlayerBase.kt @@ -52,10 +52,13 @@ import com.futo.platformplayer.api.media.platforms.js.models.sources.JSDashManif import com.futo.platformplayer.api.media.platforms.js.models.sources.JSHLSManifestAudioSource import com.futo.platformplayer.api.media.platforms.js.models.sources.JSSource import com.futo.platformplayer.api.media.platforms.js.models.sources.JSVideoUrlRangeSource +import com.futo.platformplayer.constructs.Event0 import com.futo.platformplayer.constructs.Event1 +import com.futo.platformplayer.engine.exceptions.ScriptReloadRequiredException import com.futo.platformplayer.helpers.VideoHelper import com.futo.platformplayer.logging.Logger import com.futo.platformplayer.states.StateApp +import com.futo.platformplayer.states.StatePlatform import com.futo.platformplayer.video.PlayerManager import com.futo.platformplayer.views.video.datasources.PluginMediaDrmCallback import com.futo.platformplayer.views.video.datasources.JSHttpDataSource @@ -108,6 +111,8 @@ abstract class FutoVideoPlayerBase : RelativeLayout { val onPositionDiscontinuity = Event1(); val onDatasourceError = Event1(); + val onReloadRequired = Event0(); + private var _didCallSourceChange = false; private var _lastState: Int = -1; @@ -585,6 +590,12 @@ abstract class FutoVideoPlayerBase : RelativeLayout { } } } + catch(reloadRequired: ScriptReloadRequiredException) { + Logger.i(TAG, "Reload required detected"); + StatePlatform.instance.handleReloadRequired(reloadRequired, { + onReloadRequired.emit(); + }); + } catch(ex: Throwable) { Logger.e(TAG, "DashRaw generator failed", ex); } @@ -677,15 +688,29 @@ abstract class FutoVideoPlayerBase : RelativeLayout { DefaultHttpDataSource.Factory().setUserAgent(DEFAULT_USER_AGENT); if(audioSource.hasGenerate) { findViewTreeLifecycleOwner()?.lifecycle?.coroutineScope?.launch(Dispatchers.IO) { - val generated = audioSource.generate(); - if(generated != null) { - withContext(Dispatchers.Main) { - _lastVideoMediaSource = DashMediaSource.Factory(dataSource) - .createMediaSource(DashManifestParser().parse(Uri.parse(audioSource.url), - ByteArrayInputStream(generated?.toByteArray() ?: ByteArray(0)))); - loadSelectedSources(play, resume); + try { + val generated = audioSource.generate(); + if(generated != null) { + withContext(Dispatchers.Main) { + _lastVideoMediaSource = DashMediaSource.Factory(dataSource) + .createMediaSource(DashManifestParser().parse(Uri.parse(audioSource.url), + ByteArrayInputStream(generated?.toByteArray() ?: ByteArray(0)))); + loadSelectedSources(play, resume); + } } } + catch(reloadRequired: ScriptReloadRequiredException) { + Logger.i(TAG, "Reload required detected"); + val plugin = audioSource.getUnderlyingPlugin(); + if(plugin == null) + return@launch; + StatePlatform.instance.reEnableClient(plugin.id, { + onReloadRequired.emit(); + }); + } + catch(ex: Throwable) { + + } } return false; }