Cleanup, fixes, clearCookies support on httpClients

This commit is contained in:
Kelvin K 2025-06-16 17:46:00 +02:00
commit ff531b5e77
9 changed files with 99 additions and 141 deletions

View file

@ -56,6 +56,7 @@ class DevJSClient : JSClient {
override fun getCopy(privateCopy: Boolean, noSaveState: Boolean): JSClient { override fun getCopy(privateCopy: Boolean, noSaveState: Boolean): JSClient {
val client = DevJSClient(_context, descriptor, _script, if(!privateCopy) _auth else null, _captcha, if (noSaveState) null else saveState(), devID); val client = DevJSClient(_context, descriptor, _script, if(!privateCopy) _auth else null, _captcha, if (noSaveState) null else saveState(), devID);
client.setReloadData(getReloadData(true));
if (noSaveState) if (noSaveState)
client.initialize() client.initialize()
return client return client

View file

@ -84,6 +84,8 @@ open class JSClient : IPlatformClient {
private var _channelCapabilities: ResultCapabilities? = null; private var _channelCapabilities: ResultCapabilities? = null;
private var _peekChannelTypes: List<String>? = null; private var _peekChannelTypes: List<String>? = null;
private var _usedReloadData: String? = null;
protected val _script: String; protected val _script: String;
private var _initialized: Boolean = false; private var _initialized: Boolean = false;
@ -198,6 +200,7 @@ open class JSClient : IPlatformClient {
open fun getCopy(withoutCredentials: Boolean = false, noSaveState: Boolean = false): JSClient { open fun getCopy(withoutCredentials: Boolean = false, noSaveState: Boolean = false): JSClient {
val client = JSClient(_context, descriptor, if (noSaveState) null else saveState(), _script, withoutCredentials); val client = JSClient(_context, descriptor, if (noSaveState) null else saveState(), _script, withoutCredentials);
client.setReloadData(getReloadData(true));
if (noSaveState) if (noSaveState)
client.initialize() client.initialize()
return client return client
@ -215,19 +218,29 @@ open class JSClient : IPlatformClient {
} }
fun setReloadData(data: String?) { fun setReloadData(data: String?) {
if(data == null) {
if(declareOnEnable.containsKey("__reloadData"))
declareOnEnable.remove("__reloadData");
}
else
declareOnEnable.put("__reloadData", data ?: ""); declareOnEnable.put("__reloadData", data ?: "");
} }
fun getReloadData(orLast: Boolean): String? {
if(declareOnEnable.containsKey("__reloadData"))
return declareOnEnable["__reloadData"];
else if(orLast)
return _usedReloadData;
return null;
}
override fun initialize() { override fun initialize() {
if (_initialized) return if (_initialized) return
Logger.i(TAG, "Plugin [${config.name}] initializing");
plugin.start(); plugin.start();
Logger.i(TAG, "Plugin [${config.name}] started");
plugin.execute("plugin.config = ${Json.encodeToString(config)}"); plugin.execute("plugin.config = ${Json.encodeToString(config)}");
plugin.execute("plugin.settings = parseSettings(${Json.encodeToString(descriptor.getSettingsWithDefaults())})"); plugin.execute("plugin.settings = parseSettings(${Json.encodeToString(descriptor.getSettingsWithDefaults())})");
Logger.i(TAG, "Plugin [${config.name}] configs set");
descriptor.appSettings.loadDefaults(descriptor.config); descriptor.appSettings.loadDefaults(descriptor.config);
@ -255,7 +268,6 @@ open class JSClient : IPlatformClient {
hasGetChannelPlaylists = plugin.executeBoolean("!!source.getChannelPlaylists") ?: false, hasGetChannelPlaylists = plugin.executeBoolean("!!source.getChannelPlaylists") ?: false,
hasGetContentRecommendations = plugin.executeBoolean("!!source.getContentRecommendations") ?: false hasGetContentRecommendations = plugin.executeBoolean("!!source.getContentRecommendations") ?: false
); );
Logger.i(TAG, "Plugin [${config.name}] capabilities retrieved");
try { try {
if (capabilities.hasGetChannelTemplateByClaimMap) if (capabilities.hasGetChannelTemplateByClaimMap)
@ -277,8 +289,11 @@ open class JSClient : IPlatformClient {
} }
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")) if(declareOnEnable.containsKey("__reloadData")) {
Logger.i(TAG, "Plugin [${config.name}] enabled with reload data: ${declareOnEnable["__reloadData"]}");
_usedReloadData = declareOnEnable["__reloadData"];
declareOnEnable.remove("__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")

View file

@ -67,9 +67,28 @@ class JSHttpClient : ManagedHttpClient {
} }
fun resetAuthCookies() {
_currentCookieMap.clear();
if(!_auth?.cookieMap.isNullOrEmpty()) {
for(domainCookies in _auth!!.cookieMap!!)
_currentCookieMap.put(domainCookies.key, HashMap(domainCookies.value));
}
if(!_captcha?.cookieMap.isNullOrEmpty()) {
for(domainCookies in _captcha!!.cookieMap!!) {
if(_currentCookieMap.containsKey(domainCookies.key))
_currentCookieMap[domainCookies.key]?.putAll(domainCookies.value);
else
_currentCookieMap.put(domainCookies.key, HashMap(domainCookies.value));
}
}
}
fun clearOtherCookies() {
_otherCookieMap.clear();
}
override fun clone(): ManagedHttpClient { override fun clone(): ManagedHttpClient {
val newClient = JSHttpClient(_jsClient, _auth); val newClient = JSHttpClient(_jsClient, _auth);
//newClient._currentCookieMap = HashMap(_currentCookieMap.toList().associate { Pair(it.first, HashMap(it.second)) }) newClient._currentCookieMap = HashMap(_currentCookieMap.toList().associate { Pair(it.first, HashMap(it.second)) })
return newClient; return newClient;
} }

View file

@ -16,7 +16,7 @@ class JSRequestModifier: IRequestModifier {
private val _plugin: JSClient; private val _plugin: JSClient;
private val _config: IV8PluginConfig; private val _config: IV8PluginConfig;
private var _modifier: V8ValueObject; private var _modifier: V8ValueObject;
override var allowByteSkip: Boolean; override var allowByteSkip: Boolean = false;
constructor(plugin: JSClient, modifier: V8ValueObject) { constructor(plugin: JSClient, modifier: V8ValueObject) {
this._plugin = plugin; this._plugin = plugin;
@ -24,24 +24,29 @@ class JSRequestModifier: IRequestModifier {
this._config = plugin.config; this._config = plugin.config;
val config = plugin.config; val config = plugin.config;
plugin.busy {
allowByteSkip = modifier.getOrNull(config, "allowByteSkip", "JSRequestModifier") ?: true; allowByteSkip = modifier.getOrNull(config, "allowByteSkip", "JSRequestModifier") ?: true;
if(!modifier.has("modifyRequest")) if(!modifier.has("modifyRequest"))
throw ScriptImplementationException(config, "RequestModifier is missing modifyRequest", null); throw ScriptImplementationException(config, "RequestModifier is missing modifyRequest", null);
} }
}
override fun modifyRequest(url: String, headers: Map<String, String>): IRequest { override fun modifyRequest(url: String, headers: Map<String, String>): IRequest {
if (_modifier.isClosed) { if (_modifier.isClosed) {
return Request(url, headers); return Request(url, headers);
} }
return _plugin.busy {
val result = V8Plugin.catchScriptErrors<Any>(_config, "[${_config.name}] JSRequestModifier", "builder.modifyRequest()") { val result = V8Plugin.catchScriptErrors<Any>(_config, "[${_config.name}] JSRequestModifier", "builder.modifyRequest()") {
_modifier.invoke("modifyRequest", url, headers); _modifier.invoke("modifyRequest", url, headers);
} as V8ValueObject; } as V8ValueObject;
val req = JSRequest(_plugin, result, url, headers); val req = JSRequest(_plugin, result, url, headers);
result.close(); result.close();
return req; return@busy req;
}
} }

View file

@ -62,9 +62,11 @@ abstract class JSSource {
if (!hasRequestModifier || _obj.isClosed) if (!hasRequestModifier || _obj.isClosed)
return null; return null;
val result = V8Plugin.catchScriptErrors<Any>(_config, "[${_config.name}] JSVideoUrlSource", "obj.getRequestModifier()") { val result = _plugin.isBusyWith("getRequestModifier") {
V8Plugin.catchScriptErrors<Any>(_config, "[${_config.name}] JSVideoUrlSource", "obj.getRequestModifier()") {
_obj.invoke("getRequestModifier", arrayOf<Any>()); _obj.invoke("getRequestModifier", arrayOf<Any>());
}; };
}
if (result !is V8ValueObject) if (result !is V8ValueObject)
return null; return null;
@ -76,13 +78,12 @@ abstract class JSSource {
return null; return null;
Logger.v("JSSource", "Request executor for [${type}] requesting"); Logger.v("JSSource", "Request executor for [${type}] requesting");
val result = V8Plugin.catchScriptErrors<Any>(_config, "[${_config.name}] JSSource", "obj.getRequestExecutor()") { val result =_plugin.isBusyWith("getRequestExecutor") {
_plugin.isBusyWith("getRequestExecutor") { V8Plugin.catchScriptErrors<Any>(_config, "[${_config.name}] JSSource", "obj.getRequestExecutor()") {
_plugin.getUnderlyingPlugin().busy {
_obj.invoke("getRequestExecutor", arrayOf<Any>()); _obj.invoke("getRequestExecutor", arrayOf<Any>());
}
}
}; };
}
Logger.v("JSSource", "Request executor for [${type}] received"); Logger.v("JSSource", "Request executor for [${type}] received");
if (result !is V8ValueObject) if (result !is V8ValueObject)

View file

@ -1,15 +1,12 @@
package com.futo.platformplayer.engine package com.futo.platformplayer.engine
import android.content.Context import android.content.Context
import com.caoccao.javet.entities.JavetEntityError
import com.caoccao.javet.exceptions.JavetCompilationException import com.caoccao.javet.exceptions.JavetCompilationException
import com.caoccao.javet.exceptions.JavetException import com.caoccao.javet.exceptions.JavetException
import com.caoccao.javet.exceptions.JavetExecutionException import com.caoccao.javet.exceptions.JavetExecutionException
import com.caoccao.javet.interfaces.IJavetEntityError import com.caoccao.javet.interfaces.IJavetEntityError
import com.caoccao.javet.interop.V8Host import com.caoccao.javet.interop.V8Host
import com.caoccao.javet.interop.V8Runtime import com.caoccao.javet.interop.V8Runtime
import com.caoccao.javet.interop.options.V8Flags
import com.caoccao.javet.interop.options.V8RuntimeOptions
import com.caoccao.javet.values.V8Value import com.caoccao.javet.values.V8Value
import com.caoccao.javet.values.primitive.V8ValueBoolean import com.caoccao.javet.values.primitive.V8ValueBoolean
import com.caoccao.javet.values.primitive.V8ValueInteger import com.caoccao.javet.values.primitive.V8ValueInteger
@ -17,7 +14,6 @@ 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
@ -44,7 +40,6 @@ import com.futo.platformplayer.logging.Logger
import com.futo.platformplayer.states.StateAssets import com.futo.platformplayer.states.StateAssets
import com.futo.platformplayer.warnIfMainThread import com.futo.platformplayer.warnIfMainThread
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.Semaphore
import java.util.concurrent.locks.ReentrantLock import java.util.concurrent.locks.ReentrantLock
import kotlin.concurrent.withLock import kotlin.concurrent.withLock
@ -58,7 +53,7 @@ class V8Plugin {
val httpClientAuth: ManagedHttpClient get() = _clientAuth; val httpClientAuth: ManagedHttpClient get() = _clientAuth;
val httpClientOthers: Map<String, JSHttpClient> get() = _clientOthers; val httpClientOthers: Map<String, JSHttpClient> get() = _clientOthers;
var startId: Int = 0; var runtimeId: Int = 0;
fun registerHttpClient(client: JSHttpClient) { fun registerHttpClient(client: JSHttpClient) {
synchronized(_clientOthers) { synchronized(_clientOthers) {
@ -76,11 +71,8 @@ class V8Plugin {
var isStopped = true; var isStopped = true;
val onStopped = Event1<V8Plugin>(); val onStopped = Event1<V8Plugin>();
//TODO: Implement a more universal isBusy system for plugins + JSClient + pooling? TBD if propagation would be beneficial private val _busyLock = ReentrantLock()
//private val _busyCounterLock = Object(); val isBusy get() = _busyLock.isLocked;
//private var _busyCounter = 0;
private val _busyLock = ReentrantLock()//Semaphore(1);
val isBusy get() = _busyLock.isLocked;//synchronized(_busyCounterLock) { _busyCounter > 0 };
var allowDevSubmit: Boolean = false var allowDevSubmit: Boolean = false
private set(value) { private set(value) {
@ -150,24 +142,19 @@ class V8Plugin {
synchronized(_runtimeLock) { synchronized(_runtimeLock) {
if (_runtime != null) if (_runtime != null)
return; return;
startId + 1; runtimeId = runtimeId + 1;
//V8RuntimeOptions.V8_FLAGS.setUseStrict(true); //V8RuntimeOptions.V8_FLAGS.setUseStrict(true);
val host = V8Host.getV8Instance(); val host = V8Host.getV8Instance();
val options = host.jsRuntimeType.getRuntimeOptions(); val options = host.jsRuntimeType.getRuntimeOptions();
Logger.i(TAG, "Plugin [${config.name}] start: Creating runtime")
_runtime = host.createV8Runtime(options); _runtime = host.createV8Runtime(options);
if (!host.isIsolateCreated) if (!host.isIsolateCreated)
throw IllegalStateException("Isolate not created"); throw IllegalStateException("Isolate not created");
Logger.i(TAG, "Plugin [${config.name}] start: Created runtime")
//Setup bridge //Setup bridge
_runtime?.let { _runtime?.let {
it.converter = V8Converter(); it.converter = V8Converter();
Logger.i(TAG, "Plugin [${config.name}] start: Loading packages")
for (pack in _depsPackages) { for (pack in _depsPackages) {
if (pack.variableName != null) if (pack.variableName != null)
it.createV8ValueObject().use { v8valueObject -> it.createV8ValueObject().use { v8valueObject ->
@ -180,8 +167,6 @@ class V8Plugin {
} }
} }
Logger.i(TAG, "Plugin [${config.name}] start: Loading deps")
//Load deps //Load deps
for (dep in _deps) for (dep in _deps)
catchScriptErrors("Dep[${dep.key}]") { catchScriptErrors("Dep[${dep.key}]") {
@ -192,13 +177,11 @@ class V8Plugin {
if (config.allowEval) if (config.allowEval)
it.allowEval(true); it.allowEval(true);
Logger.i(TAG, "Plugin [${config.name}] start: Loading script")
//Load plugin //Load plugin
catchScriptErrors("Plugin[${config.name}]") { catchScriptErrors("Plugin[${config.name}]") {
it.getExecutor(script).executeVoid() it.getExecutor(script).executeVoid()
}; };
isStopped = false; isStopped = false;
Logger.i(TAG, "Plugin [${config.name}] start: Script loaded")
} }
} }
} }
@ -210,7 +193,7 @@ class V8Plugin {
if(isStopped) if(isStopped)
return@busy; return@busy;
isStopped = true; isStopped = true;
startId = -1; runtimeId = runtimeId + 1;
//Cleanup http //Cleanup http
for(pack in _depsPackages) { for(pack in _depsPackages) {
@ -260,79 +243,11 @@ class V8Plugin {
runtime.getExecutor(js).execute() runtime.getExecutor(js).execute()
}; };
} }
/*
synchronized(_busyCounterLock) {
_busyCounter++;
}
val runtime = _runtime ?: throw IllegalStateException("JSPlugin not started yet");
try {
return catchScriptErrors("Plugin[${config.name}]", js) {
runtime.getExecutor(js).execute()
};
}
finally {
synchronized(_busyCounterLock) {
//Free busy *after* afterBusy calls are done to prevent calls on dead runtimes
try {
afterBusy.emit(_busyCounter - 1);
}
catch(ex: Throwable) {
Logger.e(TAG, "Unhandled V8Plugin.afterBusy", ex);
}
_busyCounter--;
}
}
*/
} }
fun executeBoolean(js: String) : Boolean? = busy { catchScriptErrors("Plugin[${config.name}]") { executeTyped<V8ValueBoolean>(js).value } } fun executeBoolean(js: String) : Boolean? = busy { catchScriptErrors("Plugin[${config.name}]") { executeTyped<V8ValueBoolean>(js).value } }
fun executeString(js: String) : String? = busy { catchScriptErrors("Plugin[${config.name}]") { executeTyped<V8ValueString>(js).value } } fun executeString(js: String) : String? = busy { catchScriptErrors("Plugin[${config.name}]") { executeTyped<V8ValueString>(js).value } }
fun executeInteger(js: String) : Int? = busy { catchScriptErrors("Plugin[${config.name}]") { executeTyped<V8ValueInteger>(js).value } } fun executeInteger(js: String) : Int? = busy { catchScriptErrors("Plugin[${config.name}]") { executeTyped<V8ValueInteger>(js).value } }
/*
fun <T> whenNotBusyBlocking(handler: (V8Plugin)->T): T {
while(true) {
synchronized(_busyCounterLock) {
if(_busyCounter == 0)
{
return handler(this);
}
}
Thread.sleep(1);
}
}
*/
/*
fun whenNotBusy(handler: (V8Plugin)->Unit) {
synchronized(_busyCounterLock) {
if(_busyCounter == 0)
handler(this);
else {
val tag = Object();
afterBusy.subscribe(tag) {
if(it == 0) {
Logger.w(TAG, "V8Plugin afterBusy handled");
afterBusy.remove(tag);
var failed = false;
synchronized(_busyCounterLock) {
if(_busyCounter > 0) {
failed = true;
return@synchronized
}
handler(this);
}
if(failed)
busy {
handler(this);
}
}
}
}
}
}
*/
private fun getPackage(packageName: String, allowNull: Boolean = false): V8Package? { private fun getPackage(packageName: String, allowNull: Boolean = false): V8Package? {
//TODO: Auto get all package types? //TODO: Auto get all package types?
return when(packageName) { return when(packageName) {
@ -397,15 +312,12 @@ class V8Plugin {
val obj = executeEx.scriptingError?.context as IJavetEntityError val obj = executeEx.scriptingError?.context as IJavetEntityError
if(obj.context.containsKey("plugin_type") == true) { if(obj.context.containsKey("plugin_type") == true) {
val pluginType = obj.context["plugin_type"].toString(); val pluginType = obj.context["plugin_type"].toString();
//val pluginType = obj.get<V8ValueString>("plugin_type").toString();
//Captcha //Captcha
if (pluginType == "CaptchaRequiredException") { if (pluginType == "CaptchaRequiredException") {
throw ScriptCaptchaRequiredException(config, throw ScriptCaptchaRequiredException(config,
obj.context["url"]?.toString(), obj.context["url"]?.toString(),
obj.context["body"]?.toString(), obj.context["body"]?.toString(),
//obj.get<V8ValueString>("url")?.toString(),
//obj.get<V8ValueString>("body")?.toString(),
executeEx, executeEx.scriptingError?.stack, codeStripped); executeEx, executeEx.scriptingError?.stack, codeStripped);
} }
@ -414,8 +326,6 @@ class V8Plugin {
throw ScriptReloadRequiredException(config, throw ScriptReloadRequiredException(config,
obj.context["msg"]?.toString(), obj.context["msg"]?.toString(),
obj.context["reloadData"]?.toString(), obj.context["reloadData"]?.toString(),
//obj.get<V8ValueString>("message")?.toString(),
//obj.get<V8ValueString>("reloadData")?.toString(),
executeEx, executeEx.scriptingError?.stack, codeStripped); executeEx, executeEx.scriptingError?.stack, codeStripped);
} }
@ -481,9 +391,4 @@ class V8Plugin {
return StateAssets.readAsset(context, path) ?: throw java.lang.IllegalStateException("script ${path} not found"); return StateAssets.readAsset(context, path) ?: throw java.lang.IllegalStateException("script ${path} not found");
} }
} }
/**
* Methods available for scripts (bridge object)
*/
} }

View file

@ -82,7 +82,8 @@ class PackageBridge : V8Package {
@V8Property @V8Property
fun supportedFeatures(): Array<String> { fun supportedFeatures(): Array<String> {
return arrayOf( return arrayOf(
"ReloadRequiredException" "ReloadRequiredException",
"HttpBatchClient"
); );
} }
@ -130,14 +131,10 @@ class PackageBridge : V8Package {
timeoutMap.remove(id); timeoutMap.remove(id);
} }
try { try {
Logger.v(TAG, "Timeout started [${id}]");
_plugin.busy { _plugin.busy {
Logger.v(TAG, "Timeout call started [${id}]");
if(!_plugin.isStopped) if(!_plugin.isStopped)
funcClone.callVoid(null, arrayOf<Any>()); funcClone.callVoid(null, arrayOf<Any>());
Logger.v(TAG, "Timeout call ended [${id}]");
} }
Logger.v(TAG, "Timeout resolved [${id}]");
} }
catch(ex: Throwable) { catch(ex: Throwable) {
Logger.e(TAG, "Failed timeout callback", ex); Logger.e(TAG, "Failed timeout callback", ex);
@ -173,7 +170,7 @@ class PackageBridge : V8Package {
Logger.i(TAG, "Plugin toast [${_config.name}]: ${str}"); Logger.i(TAG, "Plugin toast [${_config.name}]: ${str}");
StateApp.instance.scopeOrNull?.launch(Dispatchers.Main) { StateApp.instance.scopeOrNull?.launch(Dispatchers.Main) {
try { try {
UIDialogs.toast(str); UIDialogs.appToast(str);
} catch (e: Throwable) { } catch (e: Throwable) {
Logger.e(TAG, "Failed to show toast.", e); Logger.e(TAG, "Failed to show toast.", e);
} }

View file

@ -347,6 +347,17 @@ class PackageHttp: V8Package {
_clientId = if(_client is JSHttpClient) _client.clientId else null; _clientId = if(_client is JSHttpClient) _client.clientId else null;
} }
@V8Function
fun resetAuthCookies(){
if(_client is JSHttpClient)
_client.resetAuthCookies();
}
@V8Function
fun clearOtherCookies(){
if(_client is JSHttpClient)
_client.clearOtherCookies();
}
@V8Function @V8Function
fun setDefaultHeaders(defaultHeaders: Map<String, String>) { fun setDefaultHeaders(defaultHeaders: Map<String, String>) {
for(pair in defaultHeaders) for(pair in defaultHeaders)

View file

@ -567,7 +567,7 @@ abstract class FutoVideoPlayerBase : RelativeLayout {
findViewTreeLifecycleOwner()?.lifecycle?.coroutineScope?.launch(Dispatchers.IO) { findViewTreeLifecycleOwner()?.lifecycle?.coroutineScope?.launch(Dispatchers.IO) {
var startId = -1; var startId = -1;
try { try {
startId = videoSource?.getUnderlyingPlugin()?.getUnderlyingPlugin()?.startId ?: -1; startId = videoSource?.getUnderlyingPlugin()?.getUnderlyingPlugin()?.runtimeId ?: -1;
val generated = videoSource.generate(); val generated = videoSource.generate();
if (generated != null) { if (generated != null) {
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
@ -597,7 +597,7 @@ abstract class FutoVideoPlayerBase : RelativeLayout {
val plugin = videoSource.getUnderlyingPlugin(); val plugin = videoSource.getUnderlyingPlugin();
if(plugin == null) if(plugin == null)
return@launch; return@launch;
if(startId != -1 && plugin.getUnderlyingPlugin()?.startId != startId) if(startId != -1 && plugin.getUnderlyingPlugin()?.runtimeId != startId)
return@launch; return@launch;
StatePlatform.instance.handleReloadRequired(reloadRequired, { StatePlatform.instance.handleReloadRequired(reloadRequired, {
onReloadRequired.emit(); onReloadRequired.emit();
@ -689,17 +689,17 @@ abstract class FutoVideoPlayerBase : RelativeLayout {
@OptIn(UnstableApi::class) @OptIn(UnstableApi::class)
private fun swapAudioSourceDashRaw(audioSource: JSDashManifestRawAudioSource, play: Boolean, resume: Boolean): Boolean { private fun swapAudioSourceDashRaw(audioSource: JSDashManifestRawAudioSource, play: Boolean, resume: Boolean): Boolean {
Logger.i(TAG, "Loading AudioSource [DashRaw]"); Logger.i(TAG, "Loading AudioSource [DashRaw]");
val dataSource = if(audioSource is JSSource && (audioSource.requiresCustomDatasource))
audioSource.getHttpDataSourceFactory()
else
DefaultHttpDataSource.Factory().setUserAgent(DEFAULT_USER_AGENT);
if(audioSource.hasGenerate) { if(audioSource.hasGenerate) {
findViewTreeLifecycleOwner()?.lifecycle?.coroutineScope?.launch(Dispatchers.IO) { findViewTreeLifecycleOwner()?.lifecycle?.coroutineScope?.launch(Dispatchers.IO) {
var startId = -1; var startId = -1;
try { try {
startId = audioSource.getUnderlyingPlugin()?.getUnderlyingPlugin()?.startId ?: -1; startId = audioSource.getUnderlyingPlugin()?.getUnderlyingPlugin()?.runtimeId ?: -1;
val generated = audioSource.generate(); val generated = audioSource.generate();
if(generated != null) { if(generated != null) {
val dataSource = if(audioSource is JSSource && (audioSource.requiresCustomDatasource))
audioSource.getHttpDataSourceFactory()
else
DefaultHttpDataSource.Factory().setUserAgent(DEFAULT_USER_AGENT);
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
_lastVideoMediaSource = DashMediaSource.Factory(dataSource) _lastVideoMediaSource = DashMediaSource.Factory(dataSource)
.createMediaSource(DashManifestParser().parse(Uri.parse(audioSource.url), .createMediaSource(DashManifestParser().parse(Uri.parse(audioSource.url),
@ -713,7 +713,7 @@ abstract class FutoVideoPlayerBase : RelativeLayout {
val plugin = audioSource.getUnderlyingPlugin(); val plugin = audioSource.getUnderlyingPlugin();
if(plugin == null) if(plugin == null)
return@launch; return@launch;
if(startId != -1 && plugin.getUnderlyingPlugin()?.startId != startId) if(startId != -1 && plugin.getUnderlyingPlugin()?.runtimeId != startId)
return@launch; return@launch;
StatePlatform.instance.reEnableClient(plugin.id, { StatePlatform.instance.reEnableClient(plugin.id, {
onReloadRequired.emit(); onReloadRequired.emit();
@ -726,6 +726,10 @@ abstract class FutoVideoPlayerBase : RelativeLayout {
return false; return false;
} }
else { else {
val dataSource = if(audioSource is JSSource && (audioSource.requiresCustomDatasource))
audioSource.getHttpDataSourceFactory()
else
DefaultHttpDataSource.Factory().setUserAgent(DEFAULT_USER_AGENT);
_lastVideoMediaSource = DashMediaSource.Factory(dataSource) _lastVideoMediaSource = DashMediaSource.Factory(dataSource)
.createMediaSource( .createMediaSource(
DashManifestParser().parse( DashManifestParser().parse(