mirror of
https://gitlab.futo.org/videostreaming/grayjay.git
synced 2025-09-22 09:19:00 +00:00
WIP: V8 update, package http fixes, ReloadRequiredException support, other fixes. Currently broken in situations where setTimeout is used
This commit is contained in:
parent
4702787784
commit
58c9aeb1a2
13 changed files with 205 additions and 44 deletions
|
@ -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)
|
implementation 'com.google.code.gson:gson:2.10.1' //Used for complex/anonymous cases like during development conversions (eg. V8RemoteObject)
|
||||||
|
|
||||||
//JS
|
//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
|
//Exoplayer
|
||||||
implementation 'androidx.media3:media3-exoplayer:1.2.1'
|
implementation 'androidx.media3:media3-exoplayer:1.2.1'
|
||||||
|
|
|
@ -103,6 +103,12 @@ class UnavailableException extends ScriptException {
|
||||||
super("UnavailableException", msg);
|
super("UnavailableException", msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
class ReloadRequiredException extends ScriptException {
|
||||||
|
constructor(msg, reloadData) {
|
||||||
|
super("ReloadRequiredException", msg);
|
||||||
|
this.reloadData = reloadData;
|
||||||
|
}
|
||||||
|
}
|
||||||
class AgeException extends ScriptException {
|
class AgeException extends ScriptException {
|
||||||
constructor(msg) {
|
constructor(msg) {
|
||||||
super("AgeException", msg);
|
super("AgeException", msg);
|
||||||
|
|
|
@ -62,6 +62,7 @@ import com.futo.platformplayer.states.StatePlugins
|
||||||
import kotlinx.serialization.encodeToString
|
import kotlinx.serialization.encodeToString
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import java.time.OffsetDateTime
|
import java.time.OffsetDateTime
|
||||||
|
import java.util.Random
|
||||||
import kotlin.Exception
|
import kotlin.Exception
|
||||||
import kotlin.reflect.full.findAnnotations
|
import kotlin.reflect.full.findAnnotations
|
||||||
import kotlin.reflect.jvm.kotlinFunction
|
import kotlin.reflect.jvm.kotlinFunction
|
||||||
|
@ -106,6 +107,8 @@ open class JSClient : IPlatformClient {
|
||||||
return _busyAction;
|
return _busyAction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val declareOnEnable = HashMap<String, String>();
|
||||||
|
|
||||||
val settings: HashMap<String, String?> get() = descriptor.settings;
|
val settings: HashMap<String, String?> get() = descriptor.settings;
|
||||||
|
|
||||||
val flags: Array<String>;
|
val flags: Array<String>;
|
||||||
|
@ -213,6 +216,10 @@ open class JSClient : IPlatformClient {
|
||||||
return plugin.httpClientOthers[id];
|
return plugin.httpClientOthers[id];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setReloadData(data: String?) {
|
||||||
|
declareOnEnable.put("__reloadData", data ?: "");
|
||||||
|
}
|
||||||
|
|
||||||
override fun initialize() {
|
override fun initialize() {
|
||||||
if (_initialized) return
|
if (_initialized) return
|
||||||
|
|
||||||
|
@ -263,7 +270,13 @@ open class JSClient : IPlatformClient {
|
||||||
fun enable() {
|
fun enable() {
|
||||||
if(!_initialized)
|
if(!_initialized)
|
||||||
initialize();
|
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)})");
|
plugin.execute("source.enable(${Json.encodeToString(config)}, parseSettings(${Json.encodeToString(descriptor.getSettingsWithDefaults())}), ${Json.encodeToString(_injectedSaveState)})");
|
||||||
|
|
||||||
|
if(declareOnEnable.containsKey("__reloadData"))
|
||||||
|
declareOnEnable.remove("__reloadData");
|
||||||
_enabled = true;
|
_enabled = true;
|
||||||
}
|
}
|
||||||
@JSDocs(1, "source.saveState()", "Provide a string that is passed to enable for quicker startup of multiple instances")
|
@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 <T> isBusyWith(actionName: String, handle: ()->T): T {
|
|
||||||
|
fun <T> isBusyWith(actionName: String, handle: ()->T): T {
|
||||||
|
val busyId = kotlin.random.Random.nextInt(9999);
|
||||||
try {
|
try {
|
||||||
|
|
||||||
|
Logger.v(TAG, "Busy with [${actionName}] (${busyId})")
|
||||||
synchronized(_busyLock) {
|
synchronized(_busyLock) {
|
||||||
_busyCounter++;
|
_busyCounter++;
|
||||||
}
|
}
|
||||||
|
@ -748,6 +765,7 @@ open class JSClient : IPlatformClient {
|
||||||
synchronized(_busyLock) {
|
synchronized(_busyLock) {
|
||||||
_busyCounter--;
|
_busyCounter--;
|
||||||
}
|
}
|
||||||
|
Logger.v(TAG, "Busy done [${actionName}] (${busyId})")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private fun <T> isBusyWith(handle: ()->T): T {
|
private fun <T> isBusyWith(handle: ()->T): T {
|
||||||
|
|
|
@ -62,12 +62,16 @@ class JSDashManifestRawAudioSource : JSSource, IAudioSource, IJSDashManifestRawS
|
||||||
if(_plugin is DevJSClient)
|
if(_plugin is DevJSClient)
|
||||||
result = StateDeveloper.instance.handleDevCall(_plugin.devID, "DashManifestRaw", false) {
|
result = StateDeveloper.instance.handleDevCall(_plugin.devID, "DashManifestRaw", false) {
|
||||||
_plugin.getUnderlyingPlugin().catchScriptErrors("DashManifestRaw", "dashManifestRaw.generate()") {
|
_plugin.getUnderlyingPlugin().catchScriptErrors("DashManifestRaw", "dashManifestRaw.generate()") {
|
||||||
_obj.invokeString("generate");
|
_plugin.isBusyWith("dashAudio.generate") {
|
||||||
|
_obj.invokeString("generate");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
result = _plugin.getUnderlyingPlugin().catchScriptErrors("DashManifestRaw", "dashManifestRaw.generate()") {
|
result = _plugin.getUnderlyingPlugin().catchScriptErrors("DashManifestRaw", "dashManifestRaw.generate()") {
|
||||||
_obj.invokeString("generate");
|
_plugin.isBusyWith("dashAudio.generate") {
|
||||||
|
_obj.invokeString("generate");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(result != null){
|
if(result != null){
|
||||||
|
|
|
@ -67,13 +67,17 @@ open class JSDashManifestRawSource: JSSource, IVideoSource, IJSDashManifestRawSo
|
||||||
if(_plugin is DevJSClient) {
|
if(_plugin is DevJSClient) {
|
||||||
result = StateDeveloper.instance.handleDevCall(_plugin.devID, "DashManifestRawSource.generate()") {
|
result = StateDeveloper.instance.handleDevCall(_plugin.devID, "DashManifestRawSource.generate()") {
|
||||||
_plugin.getUnderlyingPlugin().catchScriptErrors("DashManifestRaw.generate", "generate()", {
|
_plugin.getUnderlyingPlugin().catchScriptErrors("DashManifestRaw.generate", "generate()", {
|
||||||
_obj.invokeString("generate");
|
_plugin.isBusyWith("dashVideo.generate") {
|
||||||
|
_obj.invokeString("generate");
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
result = _plugin.getUnderlyingPlugin().catchScriptErrors("DashManifestRaw.generate", "generate()", {
|
result = _plugin.getUnderlyingPlugin().catchScriptErrors("DashManifestRaw.generate", "generate()", {
|
||||||
_obj.invokeString("generate");
|
_plugin.isBusyWith("dashVideo.generate") {
|
||||||
|
_obj.invokeString("generate");
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if(result != null){
|
if(result != null){
|
||||||
|
|
|
@ -75,9 +75,11 @@ abstract class JSSource {
|
||||||
if (!hasRequestExecutor || _obj.isClosed)
|
if (!hasRequestExecutor || _obj.isClosed)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
Logger.v("JSSource", "Request executor for [${type}] requesting");
|
||||||
val result = V8Plugin.catchScriptErrors<Any>(_config, "[${_config.name}] JSSource", "obj.getRequestExecutor()") {
|
val result = V8Plugin.catchScriptErrors<Any>(_config, "[${_config.name}] JSSource", "obj.getRequestExecutor()") {
|
||||||
_obj.invoke("getRequestExecutor", arrayOf<Any>());
|
_obj.invoke("getRequestExecutor", arrayOf<Any>());
|
||||||
};
|
};
|
||||||
|
Logger.v("JSSource", "Request executor for [${type}] received");
|
||||||
|
|
||||||
if (result !is V8ValueObject)
|
if (result !is V8ValueObject)
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -15,6 +15,7 @@ import com.caoccao.javet.values.primitive.V8ValueString
|
||||||
import com.caoccao.javet.values.reference.V8ValueObject
|
import com.caoccao.javet.values.reference.V8ValueObject
|
||||||
import com.futo.platformplayer.api.http.ManagedHttpClient
|
import com.futo.platformplayer.api.http.ManagedHttpClient
|
||||||
import com.futo.platformplayer.api.media.platforms.js.internal.JSHttpClient
|
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.constructs.Event1
|
||||||
import com.futo.platformplayer.engine.exceptions.NoInternetException
|
import com.futo.platformplayer.engine.exceptions.NoInternetException
|
||||||
import com.futo.platformplayer.engine.exceptions.PluginEngineStoppedException
|
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.ScriptExecutionException
|
||||||
import com.futo.platformplayer.engine.exceptions.ScriptImplementationException
|
import com.futo.platformplayer.engine.exceptions.ScriptImplementationException
|
||||||
import com.futo.platformplayer.engine.exceptions.ScriptLoginRequiredException
|
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.ScriptTimeoutException
|
||||||
import com.futo.platformplayer.engine.exceptions.ScriptUnavailableException
|
import com.futo.platformplayer.engine.exceptions.ScriptUnavailableException
|
||||||
import com.futo.platformplayer.engine.internal.V8Converter
|
import com.futo.platformplayer.engine.internal.V8Converter
|
||||||
|
@ -186,6 +188,7 @@ class V8Plugin {
|
||||||
Logger.i(TAG, "Stopping plugin [${config.name}]");
|
Logger.i(TAG, "Stopping plugin [${config.name}]");
|
||||||
isStopped = true;
|
isStopped = true;
|
||||||
whenNotBusy {
|
whenNotBusy {
|
||||||
|
Logger.i(TAG, "Plugin stopping");
|
||||||
synchronized(_runtimeLock) {
|
synchronized(_runtimeLock) {
|
||||||
isStopped = true;
|
isStopped = true;
|
||||||
|
|
||||||
|
@ -200,7 +203,7 @@ class V8Plugin {
|
||||||
_runtime = null;
|
_runtime = null;
|
||||||
if(!it.isClosed && !it.isDead) {
|
if(!it.isClosed && !it.isDead) {
|
||||||
try {
|
try {
|
||||||
it.close();
|
it.close(true);
|
||||||
}
|
}
|
||||||
catch(ex: JavetException) {
|
catch(ex: JavetException) {
|
||||||
//In case race conditions are going on, already closed runtimes are fine.
|
//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, "Stopped plugin [${config.name}]");
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
Logger.i(TAG, "Plugin stopped");
|
||||||
onStopped.emit(this);
|
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);
|
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) {
|
catch(executeEx: JavetExecutionException) {
|
||||||
if(executeEx.scriptingError?.context?.containsKey("plugin_type") == true) {
|
if(executeEx.scriptingError?.context is V8ValueObject) {
|
||||||
val pluginType = executeEx.scriptingError.context["plugin_type"].toString();
|
val obj = executeEx.scriptingError?.context as V8ValueObject
|
||||||
|
if(obj.has("plugin_type") == true) {
|
||||||
|
val pluginType = obj.get<V8ValueString>("plugin_type").toString();
|
||||||
|
|
||||||
//Captcha
|
//Captcha
|
||||||
if (pluginType == "CaptchaRequiredException") {
|
if (pluginType == "CaptchaRequiredException") {
|
||||||
throw ScriptCaptchaRequiredException(config,
|
throw ScriptCaptchaRequiredException(config,
|
||||||
executeEx.scriptingError.context["url"]?.toString(),
|
obj.get<V8ValueString>("url")?.toString(),
|
||||||
executeEx.scriptingError.context["body"]?.toString(),
|
obj.get<V8ValueString>("body")?.toString(),
|
||||||
executeEx, executeEx.scriptingError?.stack, codeStripped);
|
executeEx, executeEx.scriptingError?.stack, codeStripped);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Reload Required
|
||||||
|
if (pluginType == "ReloadRequiredException") {
|
||||||
|
throw ScriptReloadRequiredException(config,
|
||||||
|
obj.get<V8ValueString>("message")?.toString(),
|
||||||
|
obj.get<V8ValueString>("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);
|
throw ScriptExecutionException(config, extractJSExceptionMessage(executeEx) ?: "", null, executeEx.scriptingError?.stack, codeStripped);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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<String>(config, "reloadData", contextName, null));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -78,6 +78,13 @@ class PackageBridge : V8Package {
|
||||||
return "android";
|
return "android";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@V8Property
|
||||||
|
fun supportedFeatures(): Array<String> {
|
||||||
|
return arrayOf(
|
||||||
|
"ReloadRequiredException"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@V8Property
|
@V8Property
|
||||||
fun supportedContent(): Array<Int> {
|
fun supportedContent(): Array<Int> {
|
||||||
return arrayOf(
|
return arrayOf(
|
||||||
|
|
|
@ -44,6 +44,17 @@ class PackageHttp: V8Package {
|
||||||
private val aliveSockets = mutableListOf<SocketResult>();
|
private val aliveSockets = mutableListOf<SocketResult>();
|
||||||
private var _cleanedUp = false;
|
private var _cleanedUp = false;
|
||||||
|
|
||||||
|
private val _clients = mutableMapOf<String, PackageHttpClient>()
|
||||||
|
|
||||||
|
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) {
|
constructor(plugin: V8Plugin, config: IV8PluginConfig): super(plugin) {
|
||||||
_config = config;
|
_config = config;
|
||||||
|
@ -112,6 +123,8 @@ class PackageHttp: V8Package {
|
||||||
_plugin.registerHttpClient(httpClient);
|
_plugin.registerHttpClient(httpClient);
|
||||||
val client = PackageHttpClient(this, httpClient);
|
val client = PackageHttpClient(this, httpClient);
|
||||||
|
|
||||||
|
_clients.put(client.clientId() ?: "", client);
|
||||||
|
|
||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
@V8Function
|
@V8Function
|
||||||
|
@ -246,18 +259,18 @@ class PackageHttp: V8Package {
|
||||||
|
|
||||||
@V8Function
|
@V8Function
|
||||||
fun request(method: String, url: String, headers: MutableMap<String, String> = HashMap(), useAuth: Boolean = false) : BatchBuilder {
|
fun request(method: String, url: String, headers: MutableMap<String, String> = HashMap(), useAuth: Boolean = false) : BatchBuilder {
|
||||||
return clientRequest(_package.getDefaultClient(useAuth), method, url, headers);
|
return clientRequest(_package.getDefaultClient(useAuth).clientId(), method, url, headers);
|
||||||
}
|
}
|
||||||
@V8Function
|
@V8Function
|
||||||
fun requestWithBody(method: String, url: String, body:String, headers: MutableMap<String, String> = HashMap(), useAuth: Boolean = false) : BatchBuilder {
|
fun requestWithBody(method: String, url: String, body:String, headers: MutableMap<String, String> = 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
|
@V8Function
|
||||||
fun GET(url: String, headers: MutableMap<String, String> = HashMap(), useAuth: Boolean = false) : BatchBuilder
|
fun GET(url: String, headers: MutableMap<String, String> = HashMap(), useAuth: Boolean = false) : BatchBuilder
|
||||||
= clientGET(_package.getDefaultClient(useAuth), url, headers);
|
= clientGET(_package.getDefaultClient(useAuth).clientId(), url, headers);
|
||||||
@V8Function
|
@V8Function
|
||||||
fun POST(url: String, body: String, headers: MutableMap<String, String> = HashMap(), useAuth: Boolean = false) : BatchBuilder
|
fun POST(url: String, body: String, headers: MutableMap<String, String> = HashMap(), useAuth: Boolean = false) : BatchBuilder
|
||||||
= clientPOST(_package.getDefaultClient(useAuth), url, body, headers);
|
= clientPOST(_package.getDefaultClient(useAuth).clientId(), url, body, headers);
|
||||||
|
|
||||||
@V8Function
|
@V8Function
|
||||||
fun DUMMY(): BatchBuilder {
|
fun DUMMY(): BatchBuilder {
|
||||||
|
@ -268,21 +281,21 @@ class PackageHttp: V8Package {
|
||||||
//Client-specific
|
//Client-specific
|
||||||
|
|
||||||
@V8Function
|
@V8Function
|
||||||
fun clientRequest(client: PackageHttpClient, method: String, url: String, headers: MutableMap<String, String> = HashMap()) : BatchBuilder {
|
fun clientRequest(clientId: String?, method: String, url: String, headers: MutableMap<String, String> = HashMap()) : BatchBuilder {
|
||||||
_reqs.add(Pair(client, RequestDescriptor(method, url, headers)));
|
_reqs.add(Pair(_package.getClient(clientId), RequestDescriptor(method, url, headers)));
|
||||||
return BatchBuilder(_package, _reqs);
|
return BatchBuilder(_package, _reqs);
|
||||||
}
|
}
|
||||||
@V8Function
|
@V8Function
|
||||||
fun clientRequestWithBody(client: PackageHttpClient, method: String, url: String, body:String, headers: MutableMap<String, String> = HashMap()) : BatchBuilder {
|
fun clientRequestWithBody(clientId: String?, method: String, url: String, body:String, headers: MutableMap<String, String> = HashMap()) : BatchBuilder {
|
||||||
_reqs.add(Pair(client, RequestDescriptor(method, url, headers, body)));
|
_reqs.add(Pair(_package.getClient(clientId), RequestDescriptor(method, url, headers, body)));
|
||||||
return BatchBuilder(_package, _reqs);
|
return BatchBuilder(_package, _reqs);
|
||||||
}
|
}
|
||||||
@V8Function
|
@V8Function
|
||||||
fun clientGET(client: PackageHttpClient, url: String, headers: MutableMap<String, String> = HashMap()) : BatchBuilder
|
fun clientGET(clientId: String?, url: String, headers: MutableMap<String, String> = HashMap()) : BatchBuilder
|
||||||
= clientRequest(client, "GET", url, headers);
|
= clientRequest(clientId, "GET", url, headers);
|
||||||
@V8Function
|
@V8Function
|
||||||
fun clientPOST(client: PackageHttpClient, url: String, body: String, headers: MutableMap<String, String> = HashMap()) : BatchBuilder
|
fun clientPOST(clientId: String?, url: String, body: String, headers: MutableMap<String, String> = HashMap()) : BatchBuilder
|
||||||
= clientRequestWithBody(client, "POST", url, body, headers);
|
= clientRequestWithBody(clientId, "POST", url, body, headers);
|
||||||
|
|
||||||
|
|
||||||
//Finalizer
|
//Finalizer
|
||||||
|
@ -321,6 +334,7 @@ class PackageHttp: V8Package {
|
||||||
@Transient
|
@Transient
|
||||||
private val _clientId: String?;
|
private val _clientId: String?;
|
||||||
|
|
||||||
|
|
||||||
@V8Property
|
@V8Property
|
||||||
fun clientId(): String? {
|
fun clientId(): String? {
|
||||||
return _clientId;
|
return _clientId;
|
||||||
|
|
|
@ -93,6 +93,7 @@ import com.futo.platformplayer.engine.exceptions.ScriptAgeException
|
||||||
import com.futo.platformplayer.engine.exceptions.ScriptException
|
import com.futo.platformplayer.engine.exceptions.ScriptException
|
||||||
import com.futo.platformplayer.engine.exceptions.ScriptImplementationException
|
import com.futo.platformplayer.engine.exceptions.ScriptImplementationException
|
||||||
import com.futo.platformplayer.engine.exceptions.ScriptLoginRequiredException
|
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.engine.exceptions.ScriptUnavailableException
|
||||||
import com.futo.platformplayer.exceptions.UnsupportedCastException
|
import com.futo.platformplayer.exceptions.UnsupportedCastException
|
||||||
import com.futo.platformplayer.fixHtmlLinks
|
import com.futo.platformplayer.fixHtmlLinks
|
||||||
|
@ -608,6 +609,10 @@ class VideoDetailView : ConstraintLayout {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_player.onReloadRequired.subscribe {
|
||||||
|
fetchVideo();
|
||||||
|
}
|
||||||
|
|
||||||
_player.onPlayChanged.subscribe {
|
_player.onPlayChanged.subscribe {
|
||||||
if (StateCasting.instance.activeDevice == null) {
|
if (StateCasting.instance.activeDevice == null) {
|
||||||
handlePlayChanged(it);
|
handlePlayChanged(it);
|
||||||
|
@ -3025,6 +3030,11 @@ class VideoDetailView : ConstraintLayout {
|
||||||
return@TaskHandler result;
|
return@TaskHandler result;
|
||||||
})
|
})
|
||||||
.success { setVideoDetails(it, true) }
|
.success { setVideoDetails(it, true) }
|
||||||
|
.exception<ScriptReloadRequiredException> {
|
||||||
|
StatePlatform.instance.handleReloadRequired(it, {
|
||||||
|
fetchVideo();
|
||||||
|
});
|
||||||
|
}
|
||||||
.exception<NoPlatformClientException> {
|
.exception<NoPlatformClientException> {
|
||||||
Logger.w(TAG, "exception<NoPlatformClientException>", it)
|
Logger.w(TAG, "exception<NoPlatformClientException>", it)
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ package com.futo.platformplayer.states
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.collection.LruCache
|
import androidx.collection.LruCache
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
import com.futo.platformplayer.R
|
import com.futo.platformplayer.R
|
||||||
import com.futo.platformplayer.Settings
|
import com.futo.platformplayer.Settings
|
||||||
import com.futo.platformplayer.UIDialogs
|
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.BatchedTaskHandler
|
||||||
import com.futo.platformplayer.constructs.Event0
|
import com.futo.platformplayer.constructs.Event0
|
||||||
import com.futo.platformplayer.constructs.Event1
|
import com.futo.platformplayer.constructs.Event1
|
||||||
|
import com.futo.platformplayer.engine.exceptions.ScriptReloadRequiredException
|
||||||
import com.futo.platformplayer.fromPool
|
import com.futo.platformplayer.fromPool
|
||||||
import com.futo.platformplayer.getNowDiffDays
|
import com.futo.platformplayer.getNowDiffDays
|
||||||
import com.futo.platformplayer.getNowDiffSeconds
|
import com.futo.platformplayer.getNowDiffSeconds
|
||||||
|
@ -316,7 +318,18 @@ class StatePlatform {
|
||||||
_platformOrderPersistent.save();
|
_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) {
|
return withContext(Dispatchers.IO) {
|
||||||
val client = getClient(id);
|
val client = getClient(id);
|
||||||
if (client !is JSClient)
|
if (client !is JSClient)
|
||||||
|
@ -347,10 +360,27 @@ class StatePlatform {
|
||||||
_availableClients.removeIf { it.id == id };
|
_availableClients.removeIf { it.id == id };
|
||||||
_availableClients.add(newClient);
|
_availableClients.add(newClient);
|
||||||
}
|
}
|
||||||
|
afterReload?.invoke();
|
||||||
return@withContext newClient;
|
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<String>) {
|
suspend fun enableClient(ids: List<String>) {
|
||||||
val currentClients = getEnabledClients().map { it.id };
|
val currentClients = getEnabledClients().map { it.id };
|
||||||
|
@ -361,6 +391,9 @@ class StatePlatform {
|
||||||
* If a client is disabled, NO requests are made to said client
|
* If a client is disabled, NO requests are made to said client
|
||||||
*/
|
*/
|
||||||
suspend fun selectClients(vararg ids: String) {
|
suspend fun selectClients(vararg ids: String) {
|
||||||
|
selectClients(null, *ids);
|
||||||
|
}
|
||||||
|
suspend fun selectClients(afterLoad: (() -> Unit)?, vararg ids: String) {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
synchronized(_clientsLock) {
|
synchronized(_clientsLock) {
|
||||||
val removed = _enabledClients.toMutableList();
|
val removed = _enabledClients.toMutableList();
|
||||||
|
@ -385,6 +418,7 @@ class StatePlatform {
|
||||||
onSourceDisabled.emit(oldClient);
|
onSourceDisabled.emit(oldClient);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
afterLoad?.invoke();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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.JSHLSManifestAudioSource
|
||||||
import com.futo.platformplayer.api.media.platforms.js.models.sources.JSSource
|
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.api.media.platforms.js.models.sources.JSVideoUrlRangeSource
|
||||||
|
import com.futo.platformplayer.constructs.Event0
|
||||||
import com.futo.platformplayer.constructs.Event1
|
import com.futo.platformplayer.constructs.Event1
|
||||||
|
import com.futo.platformplayer.engine.exceptions.ScriptReloadRequiredException
|
||||||
import com.futo.platformplayer.helpers.VideoHelper
|
import com.futo.platformplayer.helpers.VideoHelper
|
||||||
import com.futo.platformplayer.logging.Logger
|
import com.futo.platformplayer.logging.Logger
|
||||||
import com.futo.platformplayer.states.StateApp
|
import com.futo.platformplayer.states.StateApp
|
||||||
|
import com.futo.platformplayer.states.StatePlatform
|
||||||
import com.futo.platformplayer.video.PlayerManager
|
import com.futo.platformplayer.video.PlayerManager
|
||||||
import com.futo.platformplayer.views.video.datasources.PluginMediaDrmCallback
|
import com.futo.platformplayer.views.video.datasources.PluginMediaDrmCallback
|
||||||
import com.futo.platformplayer.views.video.datasources.JSHttpDataSource
|
import com.futo.platformplayer.views.video.datasources.JSHttpDataSource
|
||||||
|
@ -108,6 +111,8 @@ abstract class FutoVideoPlayerBase : RelativeLayout {
|
||||||
val onPositionDiscontinuity = Event1<Long>();
|
val onPositionDiscontinuity = Event1<Long>();
|
||||||
val onDatasourceError = Event1<Throwable>();
|
val onDatasourceError = Event1<Throwable>();
|
||||||
|
|
||||||
|
val onReloadRequired = Event0();
|
||||||
|
|
||||||
private var _didCallSourceChange = false;
|
private var _didCallSourceChange = false;
|
||||||
private var _lastState: Int = -1;
|
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) {
|
catch(ex: Throwable) {
|
||||||
Logger.e(TAG, "DashRaw generator failed", ex);
|
Logger.e(TAG, "DashRaw generator failed", ex);
|
||||||
}
|
}
|
||||||
|
@ -677,15 +688,29 @@ abstract class FutoVideoPlayerBase : RelativeLayout {
|
||||||
DefaultHttpDataSource.Factory().setUserAgent(DEFAULT_USER_AGENT);
|
DefaultHttpDataSource.Factory().setUserAgent(DEFAULT_USER_AGENT);
|
||||||
if(audioSource.hasGenerate) {
|
if(audioSource.hasGenerate) {
|
||||||
findViewTreeLifecycleOwner()?.lifecycle?.coroutineScope?.launch(Dispatchers.IO) {
|
findViewTreeLifecycleOwner()?.lifecycle?.coroutineScope?.launch(Dispatchers.IO) {
|
||||||
val generated = audioSource.generate();
|
try {
|
||||||
if(generated != null) {
|
val generated = audioSource.generate();
|
||||||
withContext(Dispatchers.Main) {
|
if(generated != null) {
|
||||||
_lastVideoMediaSource = DashMediaSource.Factory(dataSource)
|
withContext(Dispatchers.Main) {
|
||||||
.createMediaSource(DashManifestParser().parse(Uri.parse(audioSource.url),
|
_lastVideoMediaSource = DashMediaSource.Factory(dataSource)
|
||||||
ByteArrayInputStream(generated?.toByteArray() ?: ByteArray(0))));
|
.createMediaSource(DashManifestParser().parse(Uri.parse(audioSource.url),
|
||||||
loadSelectedSources(play, resume);
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue