mirror of
https://gitlab.futo.org/videostreaming/grayjay.git
synced 2025-05-29 13:00:21 +02:00
Experimentation code
This commit is contained in:
parent
3f9477c246
commit
e87a1c079c
@ -8,6 +8,7 @@ import androidx.work.WorkManager
|
|||||||
import com.caoccao.javet.values.primitive.V8ValueInteger
|
import com.caoccao.javet.values.primitive.V8ValueInteger
|
||||||
import com.caoccao.javet.values.primitive.V8ValueString
|
import com.caoccao.javet.values.primitive.V8ValueString
|
||||||
import com.futo.platformplayer.activities.DeveloperActivity
|
import com.futo.platformplayer.activities.DeveloperActivity
|
||||||
|
import com.futo.platformplayer.activities.MainActivity
|
||||||
import com.futo.platformplayer.activities.SettingsActivity
|
import com.futo.platformplayer.activities.SettingsActivity
|
||||||
import com.futo.platformplayer.api.http.ManagedHttpClient
|
import com.futo.platformplayer.api.http.ManagedHttpClient
|
||||||
import com.futo.platformplayer.api.media.models.contents.IPlatformContent
|
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"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -538,6 +538,11 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
|
|||||||
"IMPORT_OPTIONS" -> {
|
"IMPORT_OPTIONS" -> {
|
||||||
UIDialogs.showImportOptionsDialog(this);
|
UIDialogs.showImportOptionsDialog(this);
|
||||||
}
|
}
|
||||||
|
"ACTION" -> {
|
||||||
|
val action = intent.getStringExtra("ACTION");
|
||||||
|
StateDeveloper.instance.testState = "TestPlayback";
|
||||||
|
StateDeveloper.instance.testPlayback();
|
||||||
|
}
|
||||||
"TAB" -> {
|
"TAB" -> {
|
||||||
when(intent.getStringExtra("TAB")){
|
when(intent.getStringExtra("TAB")){
|
||||||
"Sources" -> {
|
"Sources" -> {
|
||||||
@ -1180,6 +1185,13 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
|
|||||||
sourcesIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
sourcesIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||||
return sourcesIntent;
|
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 {
|
fun getImportOptionsIntent(context: Context): Intent {
|
||||||
val sourcesIntent = Intent(context, MainActivity::class.java);
|
val sourcesIntent = Intent(context, MainActivity::class.java);
|
||||||
|
@ -5,6 +5,7 @@ import com.futo.platformplayer.SignatureProvider
|
|||||||
import com.futo.platformplayer.api.media.Serializer
|
import com.futo.platformplayer.api.media.Serializer
|
||||||
import com.futo.platformplayer.engine.IV8PluginConfig
|
import com.futo.platformplayer.engine.IV8PluginConfig
|
||||||
import com.futo.platformplayer.states.StatePlugins
|
import com.futo.platformplayer.states.StatePlugins
|
||||||
|
import kotlinx.serialization.Contextual
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
@ -77,7 +78,8 @@ class SourcePluginConfig(
|
|||||||
private var _allowUrlsLowerVal: List<String>? = null;
|
private var _allowUrlsLowerVal: List<String>? = null;
|
||||||
private val _allowUrlsLower: List<String> get() {
|
private val _allowUrlsLower: List<String> get() {
|
||||||
if(_allowUrlsLowerVal == null)
|
if(_allowUrlsLowerVal == null)
|
||||||
_allowUrlsLowerVal = allowUrls.map { it.lowercase() };
|
_allowUrlsLowerVal = allowUrls.map { it.lowercase() }
|
||||||
|
.filter { it.length > 0 && (it[0] != '*' || (_allowRegex.matches(it))) };
|
||||||
return _allowUrlsLowerVal!!;
|
return _allowUrlsLowerVal!!;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -170,10 +172,12 @@ class SourcePluginConfig(
|
|||||||
return true;
|
return true;
|
||||||
val uri = Uri.parse(url);
|
val uri = Uri.parse(url);
|
||||||
val host = uri.host?.lowercase() ?: "";
|
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 {
|
companion object {
|
||||||
|
private val _allowRegex = Regex("\\*\\.[a-z0-9]*\\.[a-z]*");
|
||||||
|
|
||||||
fun fromJson(json: String, sourceUrl: String? = null): SourcePluginConfig {
|
fun fromJson(json: String, sourceUrl: String? = null): SourcePluginConfig {
|
||||||
val obj = Serializer.json.decodeFromString<SourcePluginConfig>(json);
|
val obj = Serializer.json.decodeFromString<SourcePluginConfig>(json);
|
||||||
if(obj.sourceUrl == null)
|
if(obj.sourceUrl == null)
|
||||||
|
@ -35,4 +35,9 @@ class JSAudioUrlRangeSource : JSAudioUrlSource, IStreamMetaDataSource {
|
|||||||
indexEnd = _obj.getOrDefault(config, "indexEnd", contextName, null);
|
indexEnd = _obj.getOrDefault(config, "indexEnd", contextName, null);
|
||||||
audioChannels = _obj.getOrDefault(config, "audioChannels", contextName, 2) ?: 2;
|
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()
|
||||||
|
}
|
||||||
}
|
}
|
@ -33,4 +33,9 @@ class JSVideoUrlRangeSource : JSVideoUrlSource, IStreamMetaDataSource {
|
|||||||
indexStart = _obj.getOrDefault(config, "indexStart", contextName, null);
|
indexStart = _obj.getOrDefault(config, "indexStart", contextName, null);
|
||||||
indexEnd = _obj.getOrDefault(config, "indexEnd", 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()
|
||||||
|
}
|
||||||
}
|
}
|
@ -52,6 +52,7 @@ class PackageBridge : V8Package {
|
|||||||
|
|
||||||
@V8Function
|
@V8Function
|
||||||
fun toast(str: String) {
|
fun toast(str: String) {
|
||||||
|
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.toast(str);
|
||||||
|
@ -102,6 +102,7 @@ import com.futo.platformplayer.selectBestImage
|
|||||||
import com.futo.platformplayer.states.AnnouncementType
|
import com.futo.platformplayer.states.AnnouncementType
|
||||||
import com.futo.platformplayer.states.StateAnnouncement
|
import com.futo.platformplayer.states.StateAnnouncement
|
||||||
import com.futo.platformplayer.states.StateApp
|
import com.futo.platformplayer.states.StateApp
|
||||||
|
import com.futo.platformplayer.states.StateDeveloper
|
||||||
import com.futo.platformplayer.states.StateDownloads
|
import com.futo.platformplayer.states.StateDownloads
|
||||||
import com.futo.platformplayer.states.StateHistory
|
import com.futo.platformplayer.states.StateHistory
|
||||||
import com.futo.platformplayer.states.StatePlatform
|
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()
|
?.distinct()
|
||||||
?.map { x -> VideoHelper.selectBestVideoSource(videoSources.filter { x == it.height * it.width }, -1, FutoVideoPlayerBase.PREFERED_VIDEO_CONTAINERS) }
|
?.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 }))
|
?.plus(videoSources.filter { it is IHLSManifestSource || it is IDashManifestSource }))
|
||||||
?.distinct()
|
?.distinct()
|
||||||
?.filter { it != null }
|
?.filter { it != null }
|
||||||
?.toList() ?: listOf();
|
?.toList() ?: listOf() else videoSources?.toList() ?: listOf()
|
||||||
val bestAudioContainer = audioSources?.let { VideoHelper.selectBestAudioSource(it, FutoVideoPlayerBase.PREFERED_AUDIO_CONTAINERS)?.container };
|
val bestAudioContainer = audioSources?.let { VideoHelper.selectBestAudioSource(it, FutoVideoPlayerBase.PREFERED_AUDIO_CONTAINERS)?.container };
|
||||||
val bestAudioSources = audioSources
|
val bestAudioSources = if(doDedup) audioSources
|
||||||
?.filter { it.container == bestAudioContainer }
|
?.filter { it.container == bestAudioContainer }
|
||||||
?.plus(audioSources.filter { it is IHLSManifestAudioSource || it is IDashManifestSource })
|
?.plus(audioSources.filter { it is IHLSManifestAudioSource || it is IDashManifestSource })
|
||||||
?.distinct()
|
?.distinct()
|
||||||
?.toList() ?: listOf();
|
?.toList() ?: listOf() else audioSources?.toList() ?: listOf();
|
||||||
|
|
||||||
val canSetSpeed = !_isCasting || StateCasting.instance.activeDevice?.canSetSpeed == true
|
val canSetSpeed = !_isCasting || StateCasting.instance.activeDevice?.canSetSpeed == true
|
||||||
val currentPlaybackRate = if (_isCasting) StateCasting.instance.activeDevice?.speed else _player.getPlaybackRate()
|
val currentPlaybackRate = if (_isCasting) StateCasting.instance.activeDevice?.speed else _player.getPlaybackRate()
|
||||||
@ -2312,6 +2315,15 @@ class VideoDetailView : ConstraintLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateTracker(positionMilliseconds, isPlaying, false);
|
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) {
|
private fun updateTracker(positionMs: Long, isPlaying: Boolean, forceUpdate: Boolean = false) {
|
||||||
|
@ -1,11 +1,19 @@
|
|||||||
package com.futo.platformplayer.states
|
package com.futo.platformplayer.states
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import com.futo.platformplayer.SettingsDev
|
||||||
import com.futo.platformplayer.UIDialogs
|
import com.futo.platformplayer.UIDialogs
|
||||||
|
import com.futo.platformplayer.activities.MainActivity
|
||||||
import com.futo.platformplayer.api.http.server.ManagedHttpServer
|
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.developer.DeveloperEndpoints
|
||||||
import com.futo.platformplayer.engine.exceptions.ScriptExecutionException
|
import com.futo.platformplayer.engine.exceptions.ScriptExecutionException
|
||||||
|
import com.futo.platformplayer.fragment.mainactivity.main.VideoDetailView
|
||||||
import com.futo.platformplayer.logging.Logger
|
import com.futo.platformplayer.logging.Logger
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import kotlin.system.measureTimeMillis
|
import kotlin.system.measureTimeMillis
|
||||||
|
|
||||||
/***
|
/***
|
||||||
@ -23,6 +31,12 @@ class StateDeveloper {
|
|||||||
|
|
||||||
var devProxy: DevProxySettings? = null;
|
var devProxy: DevProxySettings? = null;
|
||||||
|
|
||||||
|
var testState: String? = null;
|
||||||
|
val isPlaybackTesting: Boolean get() {
|
||||||
|
return SettingsDev.instance.developerMode && testState == "TestPlayback";
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
fun initializeDev(id: String) {
|
fun initializeDev(id: String) {
|
||||||
currentDevID = id;
|
currentDevID = id;
|
||||||
synchronized(_devLogs) {
|
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 {
|
companion object {
|
||||||
const val DEV_ID = "DEV";
|
const val DEV_ID = "DEV";
|
||||||
|
|
||||||
@ -152,6 +197,7 @@ class StateDeveloper {
|
|||||||
it._server?.stop();
|
it._server?.stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@kotlinx.serialization.Serializable
|
@kotlinx.serialization.Serializable
|
||||||
|
@ -645,13 +645,14 @@ abstract class FutoVideoPlayerBase : RelativeLayout {
|
|||||||
|
|
||||||
when (error.errorCode) {
|
when (error.errorCode) {
|
||||||
PlaybackException.ERROR_CODE_IO_BAD_HTTP_STATUS -> {
|
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) {
|
if(error.cause is HttpDataSource.InvalidResponseCodeException) {
|
||||||
val cause = error.cause as HttpDataSource.InvalidResponseCodeException
|
val cause = error.cause as HttpDataSource.InvalidResponseCodeException
|
||||||
|
|
||||||
Logger.v(TAG, null) {
|
Logger.w(TAG, null) {
|
||||||
"ERROR BAD HTTP ${cause.responseCode},\n" +
|
"ERROR BAD HTTP ${cause.responseCode},\n" +
|
||||||
"Video Source: ${V8RemoteObject.gsonStandard.toJson(lastVideoSource)}\n" +
|
"Video Source: ${lastVideoSource?.toString()}\n" +
|
||||||
"Audio Source: ${V8RemoteObject.gsonStandard.toJson(lastAudioSource)}\n" +
|
"Audio Source: ${lastAudioSource?.toString()}\n" +
|
||||||
"Dash: ${_lastGeneratedDash}"
|
"Dash: ${_lastGeneratedDash}"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,7 @@ import androidx.media3.datasource.HttpDataSource;
|
|||||||
import androidx.media3.datasource.HttpUtil;
|
import androidx.media3.datasource.HttpUtil;
|
||||||
import androidx.media3.datasource.TransferListener;
|
import androidx.media3.datasource.TransferListener;
|
||||||
|
|
||||||
|
import com.futo.platformplayer.engine.dev.V8RemoteObject;
|
||||||
import com.futo.platformplayer.logging.Logger;
|
import com.futo.platformplayer.logging.Logger;
|
||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
import com.google.common.collect.ForwardingMap;
|
import com.google.common.collect.ForwardingMap;
|
||||||
@ -46,6 +47,8 @@ import java.util.Map;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.zip.GZIPInputStream;
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
|
import kotlinx.serialization.json.Json;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Based on the default ExoPlayer DefaultHttpDataSource
|
* Based on the default ExoPlayer DefaultHttpDataSource
|
||||||
*/
|
*/
|
||||||
@ -583,7 +586,7 @@ public class JSHttpDataSource extends BaseDataSource implements HttpDataSource {
|
|||||||
requestHeaders = result.getHeaders();
|
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));
|
HttpURLConnection connection = openConnection(new URL(requestUrl));
|
||||||
connection.setConnectTimeout(connectTimeoutMillis);
|
connection.setConnectTimeout(connectTimeoutMillis);
|
||||||
|
@ -477,6 +477,8 @@
|
|||||||
<string name="removes_all_subscriptions">Removes all subscriptions</string>
|
<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="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="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="subscriptions_cache_5000">Subscriptions Cache 5000</string>
|
||||||
<string name="history_cache_100">History Cache 100</string>
|
<string name="history_cache_100">History Cache 100</string>
|
||||||
<string name="start_server_on_boot">Start Server on boot</string>
|
<string name="start_server_on_boot">Start Server on boot</string>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user