Experimentation code

This commit is contained in:
Kelvin 2024-07-17 01:37:53 +02:00
parent 3f9477c246
commit e87a1c079c
11 changed files with 109 additions and 10 deletions

View file

@ -8,6 +8,7 @@ import androidx.work.WorkManager
import com.caoccao.javet.values.primitive.V8ValueInteger
import com.caoccao.javet.values.primitive.V8ValueString
import com.futo.platformplayer.activities.DeveloperActivity
import com.futo.platformplayer.activities.MainActivity
import com.futo.platformplayer.activities.SettingsActivity
import com.futo.platformplayer.api.http.ManagedHttpClient
import com.futo.platformplayer.api.media.models.contents.IPlatformContent
@ -491,6 +492,13 @@ class SettingsDev : FragmentedStorageFileJson() {
}
}
}
@FormField(R.string.test_playback, FieldForm.BUTTON,
R.string.test_playback, 1)
fun testPlayback(context: Context) {
context.startActivity(MainActivity.getActionIntent(context, "TEST_PLAYBACK"));
}
}

View file

@ -538,6 +538,11 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
"IMPORT_OPTIONS" -> {
UIDialogs.showImportOptionsDialog(this);
}
"ACTION" -> {
val action = intent.getStringExtra("ACTION");
StateDeveloper.instance.testState = "TestPlayback";
StateDeveloper.instance.testPlayback();
}
"TAB" -> {
when(intent.getStringExtra("TAB")){
"Sources" -> {
@ -1180,6 +1185,13 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
sourcesIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
return sourcesIntent;
}
fun getActionIntent(context: Context, action: String) : Intent {
val sourcesIntent = Intent(context, MainActivity::class.java);
sourcesIntent.action = "ACTION";
sourcesIntent.putExtra("ACTION", action);
sourcesIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
return sourcesIntent;
}
fun getImportOptionsIntent(context: Context): Intent {
val sourcesIntent = Intent(context, MainActivity::class.java);

View file

@ -5,6 +5,7 @@ import com.futo.platformplayer.SignatureProvider
import com.futo.platformplayer.api.media.Serializer
import com.futo.platformplayer.engine.IV8PluginConfig
import com.futo.platformplayer.states.StatePlugins
import kotlinx.serialization.Contextual
import java.net.URL
import java.util.UUID
@ -77,7 +78,8 @@ class SourcePluginConfig(
private var _allowUrlsLowerVal: List<String>? = null;
private val _allowUrlsLower: List<String> get() {
if(_allowUrlsLowerVal == null)
_allowUrlsLowerVal = allowUrls.map { it.lowercase() };
_allowUrlsLowerVal = allowUrls.map { it.lowercase() }
.filter { it.length > 0 && (it[0] != '*' || (_allowRegex.matches(it))) };
return _allowUrlsLowerVal!!;
};
@ -170,10 +172,12 @@ class SourcePluginConfig(
return true;
val uri = Uri.parse(url);
val host = uri.host?.lowercase() ?: "";
return _allowUrlsLower.any { it == host };
return _allowUrlsLower.any { it == host || (it.length > 0 && it[0] == '*' && host.endsWith(it.substring(1))) };
}
companion object {
private val _allowRegex = Regex("\\*\\.[a-z0-9]*\\.[a-z]*");
fun fromJson(json: String, sourceUrl: String? = null): SourcePluginConfig {
val obj = Serializer.json.decodeFromString<SourcePluginConfig>(json);
if(obj.sourceUrl == null)

View file

@ -35,4 +35,9 @@ class JSAudioUrlRangeSource : JSAudioUrlSource, IStreamMetaDataSource {
indexEnd = _obj.getOrDefault(config, "indexEnd", contextName, null);
audioChannels = _obj.getOrDefault(config, "audioChannels", contextName, 2) ?: 2;
}
override fun toString(): String {
return "RangeSource(url=[${getAudioUrl()}], itagId=[${itagId}], initStart=[${initStart}], initEnd=[${initEnd}], indexStart=[${indexStart}], indexEnd=[${indexEnd}]))";
return super.toString()
}
}

View file

@ -33,4 +33,9 @@ class JSVideoUrlRangeSource : JSVideoUrlSource, IStreamMetaDataSource {
indexStart = _obj.getOrDefault(config, "indexStart", contextName, null);
indexEnd = _obj.getOrDefault(config, "indexEnd", contextName, null);
}
override fun toString(): String {
return "RangeSource(url=[${getVideoUrl()}], itagId=[${itagId}], initStart=[${initStart}], initEnd=[${initEnd}], indexStart=[${indexStart}], indexEnd=[${indexEnd}]))";
return super.toString()
}
}

View file

@ -52,6 +52,7 @@ class PackageBridge : V8Package {
@V8Function
fun toast(str: String) {
Logger.i(TAG, "Plugin toast [${_config.name}]: ${str}");
StateApp.instance.scopeOrNull?.launch(Dispatchers.Main) {
try {
UIDialogs.toast(str);

View file

@ -102,6 +102,7 @@ import com.futo.platformplayer.selectBestImage
import com.futo.platformplayer.states.AnnouncementType
import com.futo.platformplayer.states.StateAnnouncement
import com.futo.platformplayer.states.StateApp
import com.futo.platformplayer.states.StateDeveloper
import com.futo.platformplayer.states.StateDownloads
import com.futo.platformplayer.states.StateHistory
import com.futo.platformplayer.states.StatePlatform
@ -1772,19 +1773,21 @@ class VideoDetailView : ConstraintLayout {
}
}
val bestVideoSources = (videoSources?.map { it.height * it.width }
val doDedup = false;
val bestVideoSources = if(doDedup) (videoSources?.map { it.height * it.width }
?.distinct()
?.map { x -> VideoHelper.selectBestVideoSource(videoSources.filter { x == it.height * it.width }, -1, FutoVideoPlayerBase.PREFERED_VIDEO_CONTAINERS) }
?.plus(videoSources.filter { it is IHLSManifestSource || it is IDashManifestSource }))
?.distinct()
?.filter { it != null }
?.toList() ?: listOf();
?.toList() ?: listOf() else videoSources?.toList() ?: listOf()
val bestAudioContainer = audioSources?.let { VideoHelper.selectBestAudioSource(it, FutoVideoPlayerBase.PREFERED_AUDIO_CONTAINERS)?.container };
val bestAudioSources = audioSources
val bestAudioSources = if(doDedup) audioSources
?.filter { it.container == bestAudioContainer }
?.plus(audioSources.filter { it is IHLSManifestAudioSource || it is IDashManifestSource })
?.distinct()
?.toList() ?: listOf();
?.toList() ?: listOf() else audioSources?.toList() ?: listOf();
val canSetSpeed = !_isCasting || StateCasting.instance.activeDevice?.canSetSpeed == true
val currentPlaybackRate = if (_isCasting) StateCasting.instance.activeDevice?.speed else _player.getPlaybackRate()
@ -2312,6 +2315,15 @@ class VideoDetailView : ConstraintLayout {
}
updateTracker(positionMilliseconds, isPlaying, false);
if(StateDeveloper.instance.isPlaybackTesting) {
if((positionMilliseconds > 1000 * 70 || positionMilliseconds > (video!!.duration * 1000 - 1000))) {
StateDeveloper.instance.testPlayback();
}
else if(video!!.duration > 70 && positionMilliseconds < 10000) {
handleSeek(40000);
}
}
}
private fun updateTracker(positionMs: Long, isPlaying: Boolean, forceUpdate: Boolean = false) {

View file

@ -1,11 +1,19 @@
package com.futo.platformplayer.states
import android.content.Context
import com.futo.platformplayer.SettingsDev
import com.futo.platformplayer.UIDialogs
import com.futo.platformplayer.activities.MainActivity
import com.futo.platformplayer.api.http.server.ManagedHttpServer
import com.futo.platformplayer.api.media.models.contents.IPlatformContent
import com.futo.platformplayer.api.media.structures.IPager
import com.futo.platformplayer.api.media.structures.PlatformContentPager
import com.futo.platformplayer.developer.DeveloperEndpoints
import com.futo.platformplayer.engine.exceptions.ScriptExecutionException
import com.futo.platformplayer.fragment.mainactivity.main.VideoDetailView
import com.futo.platformplayer.logging.Logger
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlin.system.measureTimeMillis
/***
@ -23,6 +31,12 @@ class StateDeveloper {
var devProxy: DevProxySettings? = null;
var testState: String? = null;
val isPlaybackTesting: Boolean get() {
return SettingsDev.instance.developerMode && testState == "TestPlayback";
};
fun initializeDev(id: String) {
currentDevID = id;
synchronized(_devLogs) {
@ -135,6 +149,37 @@ class StateDeveloper {
}
private var homePager: IPager<IPlatformContent>? = null;
private var pagerIndex = 0;
fun testPlayback(){
val mainActivity = if(StateApp.instance.isMainActive) StateApp.instance.context as MainActivity else return;
StateApp.instance.scope.launch(Dispatchers.IO) {
if(homePager == null)
homePager = StatePlatform.instance.getHome();
var pager = homePager ?: return@launch;
pagerIndex++;
val video = if(pager.getResults().size <= pagerIndex) {
if(!pager.hasMorePages()) {
homePager = StatePlatform.instance.getHome();
pager = homePager as IPager<IPlatformContent>;
}
pager.nextPage();
pagerIndex = 0;
val results = pager.getResults();
if(results.size <= 0)
null;
else
results[0];
}
else
pager.getResults()[pagerIndex];
StateApp.instance.scope.launch(Dispatchers.Main) {
mainActivity.navigate(mainActivity._fragVideoDetail, video);
}
}
}
companion object {
const val DEV_ID = "DEV";
@ -152,6 +197,7 @@ class StateDeveloper {
it._server?.stop();
}
}
}
@kotlinx.serialization.Serializable

View file

@ -645,13 +645,14 @@ abstract class FutoVideoPlayerBase : RelativeLayout {
when (error.errorCode) {
PlaybackException.ERROR_CODE_IO_BAD_HTTP_STATUS -> {
Logger.w(TAG, "ERROR_CODE_IO_BAD_HTTP_STATUS ${error.cause?.javaClass?.simpleName}");
if(error.cause is HttpDataSource.InvalidResponseCodeException) {
val cause = error.cause as HttpDataSource.InvalidResponseCodeException
Logger.v(TAG, null) {
Logger.w(TAG, null) {
"ERROR BAD HTTP ${cause.responseCode},\n" +
"Video Source: ${V8RemoteObject.gsonStandard.toJson(lastVideoSource)}\n" +
"Audio Source: ${V8RemoteObject.gsonStandard.toJson(lastAudioSource)}\n" +
"Video Source: ${lastVideoSource?.toString()}\n" +
"Audio Source: ${lastAudioSource?.toString()}\n" +
"Dash: ${_lastGeneratedDash}"
};
}

View file

@ -25,6 +25,7 @@ import androidx.media3.datasource.HttpDataSource;
import androidx.media3.datasource.HttpUtil;
import androidx.media3.datasource.TransferListener;
import com.futo.platformplayer.engine.dev.V8RemoteObject;
import com.futo.platformplayer.logging.Logger;
import com.google.common.base.Predicate;
import com.google.common.collect.ForwardingMap;
@ -46,6 +47,8 @@ import java.util.Map;
import java.util.Set;
import java.util.zip.GZIPInputStream;
import kotlinx.serialization.json.Json;
/*
* Based on the default ExoPlayer DefaultHttpDataSource
*/
@ -583,7 +586,7 @@ public class JSHttpDataSource extends BaseDataSource implements HttpDataSource {
requestHeaders = result.getHeaders();
}
Logger.Companion.v("JSHttpDataSource", "DataSource REQ: " + requestUrl, null);
Logger.Companion.v("JSHttpDataSource", "DataSource REQ: " + requestUrl + "\nHEADERS: [" + V8RemoteObject.Companion.getGsonStandard().toJson(requestHeaders)+ "]", null);
HttpURLConnection connection = openConnection(new URL(requestUrl));
connection.setConnectTimeout(connectTimeoutMillis);

View file

@ -477,6 +477,8 @@
<string name="removes_all_subscriptions">Removes all subscriptions</string>
<string name="settings_related_to_development_server_be_careful_as_it_may_open_your_phone_to_security_vulnerabilities">Settings related to development server, be careful as it may open your phone to security vulnerabilities</string>
<string name="start_server">Start Server</string>
<string name="test_playback">Test Playback</string>
<string name="test_playback_desc">Keeps playing videos</string>
<string name="subscriptions_cache_5000">Subscriptions Cache 5000</string>
<string name="history_cache_100">History Cache 100</string>
<string name="start_server_on_boot">Start Server on boot</string>