Cleaning up some logs, reducing retry intervals, planned live stream auto refresh, tweak some buffer timings, fixing some scopes

This commit is contained in:
Kelvin 2023-10-11 17:58:04 +02:00
parent 3a7e477e9b
commit f76a5b5f01
27 changed files with 148 additions and 81 deletions

View file

@ -333,7 +333,7 @@ class Settings : FragmentedStorageFileJson() {
"Submit logs to help us narrow down issues", 1
)
fun submitLogs() {
StateApp.instance.scopeGetter().launch(Dispatchers.IO) {
StateApp.instance.scopeOrNull?.launch(Dispatchers.IO) {
try {
if (!Logger.submitLogs()) {
withContext(Dispatchers.Main) {
@ -462,7 +462,7 @@ class Settings : FragmentedStorageFileJson() {
fun viewChangelog() {
UIDialogs.toast("Retrieving changelog");
SettingsActivity.getActivity()?.let {
StateApp.instance.scopeGetter().launch(Dispatchers.IO) {
StateApp.instance.scopeOrNull?.launch(Dispatchers.IO) {
try {
val version = StateUpdate.instance.downloadVersionCode(ManagedHttpClient()) ?: return@launch;
Logger.i(TAG, "Version retrieved $version");

View file

@ -392,7 +392,7 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
override fun onResume() {
super.onResume();
Logger.i(TAG, "onResume")
Logger.v(TAG, "onResume")
val curOrientation = _orientationManager.orientation;
@ -408,13 +408,10 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
val videoToOpen = StateSaved.instance.videoToOpen;
if (_wasStopped) {
Logger.i(TAG, "_wasStopped is true");
Logger.i(TAG, "set _wasStopped = false");
_wasStopped = false;
Logger.i(TAG, "onResume videoToOpen=$videoToOpen");
if (videoToOpen != null && _fragVideoDetail.state == VideoDetailFragment.State.CLOSED) {
Logger.i(TAG, "onResume videoToOpen=$videoToOpen");
if (StatePlatform.instance.hasEnabledVideoClient(videoToOpen.url)) {
navigate(_fragVideoDetail, UrlVideoWithTime(videoToOpen.url, videoToOpen.timeSeconds, false));
_fragVideoDetail.maximizeVideoDetail(true);
@ -427,13 +424,13 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
override fun onPause() {
super.onPause();
Logger.i(TAG, "onPause")
Logger.v(TAG, "onPause")
_isVisible = false;
}
override fun onStop() {
super.onStop()
Logger.i(TAG, "_wasStopped = true");
Logger.v(TAG, "_wasStopped = true");
_wasStopped = true;
}
@ -722,22 +719,20 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
}
_fragVideoDetail.onOrientationChanged(OrientationManager.Orientation.PORTRAIT);
}
Logger.i(TAG, "onRestart5");
}
override fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean, newConfig: Configuration) {
super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig);
val isStop: Boolean = lifecycle.currentState == Lifecycle.State.CREATED;
Logger.i(TAG, "onPictureInPictureModeChanged isInPictureInPictureMode=$isInPictureInPictureMode isStop=$isStop")
Logger.v(TAG, "onPictureInPictureModeChanged isInPictureInPictureMode=$isInPictureInPictureMode isStop=$isStop")
_fragVideoDetail?.onPictureInPictureModeChanged(isInPictureInPictureMode, isStop, newConfig);
Logger.i(TAG, "onPictureInPictureModeChanged Ready");
Logger.v(TAG, "onPictureInPictureModeChanged Ready");
}
override fun onDestroy() {
super.onDestroy();
Logger.i(TAG, "onDestroy")
Logger.v(TAG, "onDestroy")
_orientationManager.disable();

View file

@ -63,7 +63,7 @@ class ManagedHttpServer(private val _requestedPort: Int = 0) {
}
}.start();
Logger.i(TAG, "Started ${port}. \n" + getAddresses().map { it.hostAddress }.joinToString("\n"));
Logger.i(TAG, "Started HTTP Server ${port}. \n" + getAddresses().map { it.hostAddress }.joinToString("\n"));
}
@Synchronized
fun stop() {

View file

@ -94,7 +94,10 @@ class LiveChatManager {
if(_pager is JSLiveEventPager)
nextInterval = _pager.nextRequest.coerceAtLeast(800).toLong();
Logger.i(TAG, "New Live Events (${newEvents.size}) [${newEvents.map { it.type.name }.joinToString(", ")}]");
if(newEvents.size > 0)
Logger.i(TAG, "New Live Events (${newEvents.size}) [${newEvents.map { it.type.name }.joinToString(", ")}]");
else
Logger.v(TAG, "No new Live Events");
_scope.launch(Dispatchers.Main) {
try {

View file

@ -11,12 +11,14 @@ class PlatformClientPool {
private val _parent: JSClient;
private val _pool: HashMap<JSClient, Int> = hashMapOf();
private var _poolCounter = 0;
private val _poolName: String?;
var isDead: Boolean = false
private set;
val onDead = Event2<JSClient, PlatformClientPool>();
constructor(parentClient: IPlatformClient) {
constructor(parentClient: IPlatformClient, name: String? = null) {
_poolName = name;
if(parentClient !is JSClient)
throw IllegalArgumentException("Pooling only supported for JSClients right now");
Logger.i(TAG, "Pool for ${parentClient.name} was started");
@ -47,7 +49,7 @@ class PlatformClientPool {
_poolCounter++;
reserved = _pool.keys.find { !it.isBusy };
if(reserved == null && _pool.size < capacity) {
Logger.i(TAG, "Started additional [${_parent.name}] client in pool (${_pool.size + 1}/${capacity})");
Logger.i(TAG, "Started additional [${_parent.name}] client in pool [${_poolName}] (${_pool.size + 1}/${capacity})");
reserved = _parent.getCopy();
reserved?.initialize();
_pool[reserved!!] = _poolCounter;

View file

@ -1,12 +1,14 @@
package com.futo.platformplayer.api.media
class PlatformMultiClientPool {
private val _name: String;
private val _maxCap: Int;
private val _clientPools: HashMap<IPlatformClient, PlatformClientPool> = hashMapOf();
private var _isFake = false;
constructor(maxCap: Int = -1) {
constructor(name: String, maxCap: Int = -1) {
_name = name;
_maxCap = if(maxCap > 0)
maxCap
else 99;
@ -17,7 +19,7 @@ class PlatformMultiClientPool {
return parentClient;
val pool = synchronized(_clientPools) {
if(!_clientPools.containsKey(parentClient))
_clientPools[parentClient] = PlatformClientPool(parentClient).apply {
_clientPools[parentClient] = PlatformClientPool(parentClient, _name).apply {
this.onDead.subscribe { client, pool ->
synchronized(_clientPools) {
if(_clientPools[parentClient] == pool)

View file

@ -42,7 +42,6 @@ open class JSContent : IPlatformContent, IPluginSourced {
id = PlatformID.fromV8(_pluginConfig, _content.getOrThrow(config, "id", contextName));
name = HtmlCompat.fromHtml(_content.getOrThrow<String>(config, "name", contextName).decodeUnicode(), HtmlCompat.FROM_HTML_MODE_LEGACY).toString();
Logger.i("JSContent", "name=$name");
author = PlatformAuthorLink.fromV8(_pluginConfig, _content.getOrThrow(config, "author", contextName));
val datetimeInt = _content.getOrThrow<Int>(config, "datetime", contextName).toLong();

View file

@ -137,11 +137,11 @@ abstract class MultiParallelPager<T> : IPager<T>, IAsyncPager<T> {
}
}
}
Logger.i(TAG, "Pager prepare in ${timeForPage}ms");
Logger.v(TAG, "Pager prepare in ${timeForPage}ms");
val timeAwait = measureTimeMillis {
_currentResults = results.map { it.await() }.mapNotNull { it };
};
Logger.i(TAG, "Pager load in ${timeAwait}ms");
Logger.v(TAG, "Pager load in ${timeAwait}ms");
_currentResultExceptions = exceptions;
return _currentResults;

View file

@ -111,7 +111,7 @@ class ChannelContentCache {
init {
val results = pager.getResults();
Logger.i(TAG, "Caching ${results.size} subscription initial results");
Logger.i(TAG, "Caching ${results.size} subscription initial results [${pager.hashCode()}]");
scope.launch(Dispatchers.IO) {
try {
val newCacheItems = instance.cacheVideos(results);

View file

@ -64,7 +64,7 @@ class StateCasting {
}
override fun serviceResolved(event: ServiceEvent) {
Logger.i(TAG, "ChromeCast service resolved: " + event.info);
Logger.v(TAG, "ChromeCast service resolved: " + event.info);
addOrUpdateDevice(event);
}

View file

@ -91,7 +91,7 @@ class TaskHandler<TParameter, TResult> {
if (!onError.emit(e, parameter)) {
Logger.e(TAG, "Uncaught exception handled by TaskHandler.", e);
} else {
Logger.w(TAG, "Handled exception in TaskHandler invoke.", e);
//Logger.w(TAG, "Handled exception in TaskHandler invoke.", e); (Prevents duplicate logs)
}
}
}

View file

@ -57,7 +57,7 @@ class CommentDialog(context: Context?, val contextUrl: String, val ref: Protocol
val processHandle = StatePolycentric.instance.processHandle!!
val eventPointer = processHandle.post(comment, null, ref)
StateApp.instance.scopeGetter().launch(Dispatchers.IO) {
StateApp.instance.scopeOrNull?.launch(Dispatchers.IO) {
try {
processHandle.fullyBackfillServers()
} catch (e: Throwable) {

View file

@ -355,7 +355,7 @@ class PostDetailFragment : MainFragment {
processHandle.opinion(ref, Opinion.neutral);
}
StateApp.instance.scopeGetter().launch(Dispatchers.IO) {
StateApp.instance.scopeOrNull?.launch(Dispatchers.IO) {
try {
processHandle.fullyBackfillServers();
} catch (e: Throwable) {

View file

@ -306,12 +306,12 @@ class VideoDetailFragment : MainFragment {
override fun onResume() {
super.onResume();
Logger.i(TAG, "onResume");
Logger.v(TAG, "onResume");
_isActive = true;
_leavingPiP = false;
_viewDetail?.let {
Logger.i(TAG, "onResume preventPictureInPicture=false");
Logger.v(TAG, "onResume preventPictureInPicture=false");
it.preventPictureInPicture = false;
if (state != State.CLOSED) {
@ -326,7 +326,7 @@ class VideoDetailFragment : MainFragment {
}
override fun onPause() {
super.onPause();
Logger.i(TAG, "onPause");
Logger.v(TAG, "onPause");
_isActive = false;
if(!isInPictureInPicture && state != State.CLOSED)
@ -334,7 +334,7 @@ class VideoDetailFragment : MainFragment {
}
override fun onStop() {
Logger.i(TAG, "onStop");
Logger.v(TAG, "onStop");
stopIfRequired();
super.onStop();
@ -352,7 +352,7 @@ class VideoDetailFragment : MainFragment {
shouldStop = false;
}
Logger.i(TAG, "shouldStop: $shouldStop");
Logger.v(TAG, "shouldStop: $shouldStop");
if(shouldStop) {
_viewDetail?.let {
val v = it.video ?: return@let;
@ -361,13 +361,13 @@ class VideoDetailFragment : MainFragment {
_viewDetail?.onStop();
StateCasting.instance.onStop();
Logger.i(TAG, "called onStop() shouldStop: $shouldStop");
Logger.v(TAG, "called onStop() shouldStop: $shouldStop");
}
}
override fun onDestroyMainView() {
super.onDestroyMainView();
Logger.i(TAG, "onDestroyMainView");
Logger.v(TAG, "onDestroyMainView");
_viewDetail?.let {
_viewDetail = null;
it.onDestroy();

View file

@ -100,6 +100,7 @@ import kotlinx.coroutines.*
import userpackage.Protocol
import java.time.OffsetDateTime
import kotlin.collections.ArrayList
import kotlin.math.abs
import kotlin.math.roundToLong
import kotlin.streams.toList
@ -232,9 +233,19 @@ class VideoDetailView : ConstraintLayout {
private val DP_5 = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 5f, resources.displayMetrics);
private val DP_2 = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 2f, resources.displayMetrics);
private var _retryJob: Job? = null;
private var _retryCount = 0;
private val _retryIntervals: Array<Long> = arrayOf(1, 2, 4, 8, 16, 32);
//TODO: Determine better behavior, waiting 60 seconds for an error that is guaranteed to happen is a bit much. (Needed? If so, maybe need special UI for retrying)
private val _retryIntervals: Array<Long> = arrayOf(1, 1);//2, 4, 8, 16, 32);
private var _liveTryJob: Job? = null;
private val _liveStreamCheckInterval = listOf(
Pair(-10 * 60, 5 * 60), //around 10 minutes, try every 1 minute
Pair(-5 * 60, 30), //around 5 minutes, try every 30 seconds
Pair(0, 10) //around live, try every 10 seconds
);
constructor(context: Context, attrs : AttributeSet? = null) : super(context, attrs) {
inflate(context, R.layout.fragview_video_detail, this);
@ -660,7 +671,7 @@ class VideoDetailView : ConstraintLayout {
//Lifecycle
fun onResume() {
Logger.i(TAG, "onResume");
Logger.v(TAG, "onResume");
_onPauseCalled = false;
Logger.i(TAG, "_video: ${video?.name ?: "no video"}");
@ -694,7 +705,7 @@ class VideoDetailView : ConstraintLayout {
_player.updateRotateLock();
}
fun onPause() {
Logger.i(TAG, "onPause");
Logger.v(TAG, "onPause");
_onPauseCalled = true;
_taskLoadVideo.cancel();
@ -722,6 +733,8 @@ class VideoDetailView : ConstraintLayout {
_overlay_quality_selector?.hide();
_retryJob?.cancel();
_retryJob = null;
_liveTryJob?.cancel();
_liveTryJob = null;
_taskLoadVideo.cancel();
handleStop();
_didStop = true;
@ -808,6 +821,8 @@ class VideoDetailView : ConstraintLayout {
_retryJob?.cancel();
_retryJob = null;
_liveTryJob?.cancel();
_liveTryJob = null;
_retryCount = 0;
fetchVideo();
@ -897,6 +912,8 @@ class VideoDetailView : ConstraintLayout {
_retryJob?.cancel();
_retryJob = null;
_liveTryJob?.cancel();
_liveTryJob = null;
_retryCount = 0;
fetchVideo();
}
@ -1034,7 +1051,7 @@ class VideoDetailView : ConstraintLayout {
processHandle.opinion(ref, Opinion.neutral);
}
StateApp.instance.scopeGetter().launch(Dispatchers.IO) {
fragment.lifecycleScope.launch(Dispatchers.IO) {
try {
processHandle.fullyBackfillServers();
} catch (e: Throwable) {
@ -1130,6 +1147,8 @@ class VideoDetailView : ConstraintLayout {
if(video.isLive && video.live != null) {
loadLiveChat(video);
}
if(video.isLive && video.live == null && !video.video.videoSources.any())
startLiveTry(video);
updateMoreButtons();
}
@ -1259,7 +1278,7 @@ class VideoDetailView : ConstraintLayout {
//If LiveStream, set to end
if(videoSource is IDashManifestSource || videoSource is IHLSManifestSource) {
if (video?.isLive == true) {
_player.seekToEnd(5000);
_player.seekToEnd(6000);
}
val videoTracks = _player.exoPlayer?.player?.currentTracks?.groups?.firstOrNull { it.mediaTrackGroup.type == C.TRACK_TYPE_VIDEO }
@ -1961,7 +1980,7 @@ class VideoDetailView : ConstraintLayout {
}
fun setProgressBarOverlayed(isOverlayed: Boolean?) {
Logger.i(TAG, "setProgressBarOverlayed(isOverlayed: ${isOverlayed ?: "null"})");
Logger.v(TAG, "setProgressBarOverlayed(isOverlayed: ${isOverlayed ?: "null"})");
isOverlayed?.let{ _cast.setProgressBarOverlayed(it) };
if(isOverlayed == null) {
@ -2080,6 +2099,8 @@ class VideoDetailView : ConstraintLayout {
_retryCount = 0;
_retryJob?.cancel();
_retryJob = null;
_liveTryJob?.cancel();
_liveTryJob = null;
UIDialogs.showGeneralRetryErrorDialog(context, "Failed to load video (ScriptException)", it, ::fetchVideo);
}
}
@ -2090,6 +2111,8 @@ class VideoDetailView : ConstraintLayout {
_retryCount = 0;
_retryJob?.cancel();
_retryJob = null;
_liveTryJob?.cancel();
_liveTryJob = null;
UIDialogs.showGeneralRetryErrorDialog(context, "Failed to load video", it, ::fetchVideo);
}
} else TaskHandler(IPlatformVideoDetails::class.java, {fragment.lifecycleScope});
@ -2107,14 +2130,16 @@ class VideoDetailView : ConstraintLayout {
Log.i(TAG, "handleErrorOrCall _retryCount=$_retryCount, starting retry job");
_retryJob?.cancel();
_retryJob = StateApp.instance.scopeGetter().launch(Dispatchers.Main) {
_retryJob = StateApp.instance.scopeOrNull?.launch(Dispatchers.Main) {
try {
delay(_retryIntervals[_retryCount++] * 1000);
fetchVideo();
} catch (e: Throwable) {
Logger.e(TAG, "Failed to fetch video.", e)
Logger.e(TAG, "Failed to retry fetch video.", e)
}
}
_liveTryJob?.cancel();
_liveTryJob = null;
} else if (isConnected && nextVideo()) {
Log.i(TAG, "handleErrorOrCall retries failed, is connected, skipped to next video");
} else {
@ -2123,6 +2148,46 @@ class VideoDetailView : ConstraintLayout {
}
}
private fun startLiveTry(liveTryVideo: IPlatformVideoDetails) {
val datetime = liveTryVideo.datetime ?: return;
val diffSeconds = datetime.getNowDiffSeconds();
val toWait = _liveStreamCheckInterval.toList().sortedBy { abs(diffSeconds - it.first) }.firstOrNull()?.second?.toLong() ?: return;
fragment.lifecycleScope.launch(Dispatchers.Main){
UIDialogs.toast(context, "Not yet available, retrying in ${toWait}s");
}
_liveTryJob?.cancel();
_liveTryJob = fragment.lifecycleScope.launch(Dispatchers.IO) {
try {
delay(toWait * 1000);
val videoDetail = StatePlatform.instance.getContentDetails(liveTryVideo.url, true).await();
if(videoDetail !is IPlatformVideoDetails)
throw IllegalStateException("Expected media content, found ${video?.contentType}");
if(videoDetail.datetime != null && videoDetail.live == null && !videoDetail.video.videoSources.any()) {
if(videoDetail.datetime!! > OffsetDateTime.now())
withContext(Dispatchers.Main) {
UIDialogs.toast(context, "Planned in ${videoDetail.datetime?.toHumanNowDiffString(true)}");
}
startLiveTry(liveTryVideo);
_liveTryJob = null;
}
else
withContext(Dispatchers.Main) {
setVideoDetails(videoDetail);
_liveTryJob = null;
}
}
catch(ex: Throwable) {
Logger.e(TAG, "Failed to live try fetch video.", ex);
withContext(Dispatchers.Main) {
UIDialogs.toast(context, "Failed to retry for live stream");
}
}
}
}
fun applyFragment(frag: VideoDetailFragment) {
fragment = frag;
fragment.onMinimize.subscribe {

View file

@ -50,7 +50,7 @@ class MediaPlaybackService : Service() {
private var _focusRequest: AudioFocusRequest? = null;
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Logger.i(TAG, "onStartCommand");
Logger.v(TAG, "onStartCommand");
if(!FragmentedStorage.isInitialized) {
@ -122,12 +122,12 @@ class MediaPlaybackService : Service() {
}
override fun onCreate() {
Logger.i(TAG, "onCreate called");
Logger.v(TAG, "onCreate");
super.onCreate()
}
override fun onDestroy() {
Logger.i(TAG, "onDestroy called");
Logger.v(TAG, "onDestroy");
_instance = null;
MediaControlReceiver.onCloseReceived.emit();
super.onDestroy();
@ -138,7 +138,7 @@ class MediaPlaybackService : Service() {
}
fun closeMediaSession() {
Logger.i(TAG, "closeMediaSession called");
Logger.v(TAG, "closeMediaSession");
stopForeground(true);
val focusRequest = _focusRequest;
@ -159,7 +159,7 @@ class MediaPlaybackService : Service() {
}
fun updateMediaSession(videoUpdated: IPlatformVideo?) {
Logger.i(TAG, "updateMediaSession called");
Logger.v(TAG, "updateMediaSession");
var isUpdating = false;
val video: IPlatformVideo;
if(videoUpdated == null) {
@ -270,7 +270,7 @@ class MediaPlaybackService : Service() {
val notif = builder.build();
notif.flags = notif.flags or NotificationCompat.FLAG_ONGOING_EVENT or NotificationCompat.FLAG_NO_CLEAR;
Logger.i(TAG, "Updating notification bitmap=${if (bitmap != null) "not null" else "null"} channelId=${channel.id} icon=${icon} video=${video?.name ?: ""} playWhenReady=${playWhenReady} session.sessionToken=${session.sessionToken}");
Logger.i(TAG, "Updating notification bitmap=${if (bitmap != null) "yes" else "no."} channelId=${channel.id} icon=${icon} video=${video?.name ?: ""} playWhenReady=${playWhenReady} session.sessionToken=${session.sessionToken}");
startForeground(MEDIA_NOTIF_ID, notif, ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK);

View file

@ -299,7 +299,7 @@ class StateApp {
}
Logger.onLogSubmitted.subscribe {
scopeGetter().launch(Dispatchers.Main) {
scopeOrNull?.launch(Dispatchers.Main) {
try {
if (it != null) {
UIDialogs.toast("Uploaded " + (it ?: "null"), true);
@ -510,7 +510,6 @@ class StateApp {
if (_lastNetworkState != NetworkState.DISCONNECTED) {
scopeOrNull?.launch(Dispatchers.Main) {
try {
Logger.i(TAG, "onConnectionAvailable emitted");
onConnectionAvailable.emit();
} catch (e: Throwable) {
Logger.e(TAG, "Failed to emit onConnectionAvailable", e)

View file

@ -70,11 +70,11 @@ class StatePlatform {
//Pools always follow the behavior of the base client. So if user disables a plugin, it kills all pooled clients.
//Each pooled client adds additional memory usage.
//WARNING: Be careful with pooling some calls, as they might use the plugin subsequently afterwards. For example pagers might block plugins in future calls.
private val _mainClientPool = PlatformMultiClientPool(2); //Used for all main user events, generally user critical
private val _pagerClientPool = PlatformMultiClientPool(2); //Used primarily for calls that result in front-end pagers, preventing them from blocking other calls.
private val _channelClientPool = PlatformMultiClientPool(15); //Used primarily for subscription/background channel fetches
private val _trackerClientPool = PlatformMultiClientPool(1); //Used exclusively for playback trackers
private val _liveEventClientPool = PlatformMultiClientPool(1); //Used exclusively for live events
private val _mainClientPool = PlatformMultiClientPool("Main", 2); //Used for all main user events, generally user critical
private val _pagerClientPool = PlatformMultiClientPool("Pagers", 2); //Used primarily for calls that result in front-end pagers, preventing them from blocking other calls.
private val _channelClientPool = PlatformMultiClientPool("Channels", 15); //Used primarily for subscription/background channel fetches
private val _trackerClientPool = PlatformMultiClientPool("Trackers", 1); //Used exclusively for playback trackers
private val _liveEventClientPool = PlatformMultiClientPool("LiveEvents", 1); //Used exclusively for live events
private val _primaryClientPersistent = FragmentedStorage.get<StringStorage>("primaryClient");

View file

@ -25,7 +25,7 @@ class StatePlayer {
private val MIN_BUFFER_DURATION = 10000;
private val MAX_BUFFER_DURATION = 60000;
private val MIN_PLAYBACK_START_BUFFER = 500;
private val MIN_PLAYBACK_RESUME_BUFFER = 1000;
private val MIN_PLAYBACK_RESUME_BUFFER = 2500;
private val BUFFER_SIZE = 1024 * 64;
var isOpen : Boolean = false

View file

@ -27,12 +27,20 @@ class StatePlugins {
private val TAG = "StatePlugins";
private val FORCE_REINSTALL_EMBEDDED = false;
private var _isFirstEmbedUpdate = true;
private val _pluginScripts = FragmentedStorage.getDirectory<PluginScriptsDirectory>();
private var _plugins = FragmentedStorage.storeJson<SourcePluginDescriptor>("plugins")
.load();
private val iconsDir = FragmentedStorage.getDirectory<PluginIconStorage>();
private val _syncObject = Object()
private var _embeddedSources: Map<String, String>? = null
private var _embeddedSourcesDefault: List<String>? = null
private var _sourcesUnderConstruction: Map<String, ImageVariable>? = null
fun getPluginIconOrNull(id: String): ImageVariable? {
if(iconsDir.hasIcon(id))
return iconsDir.getIconBinary(id);
@ -53,18 +61,6 @@ class StatePlugins {
}
}
@Serializable
private data class PluginConfig(
val SOURCES_EMBEDDED: Map<String, String>,
val SOURCES_EMBEDDED_DEFAULT: List<String>,
val SOURCES_UNDER_CONSTRUCTION: Map<String, String>
)
private val _syncObject = Object()
private var _embeddedSources: Map<String, String>? = null
private var _embeddedSourcesDefault: List<String>? = null
private var _sourcesUnderConstruction: Map<String, ImageVariable>? = null
private fun ensureSourcesConfigLoaded(context: Context) {
if (_embeddedSources != null && _embeddedSourcesDefault != null && _sourcesUnderConstruction != null) {
return
@ -122,8 +118,11 @@ class StatePlugins {
Logger.i(TAG, "Found outdated embedded plugin [${existing.config.id}] ${existing.config.name}, deleting and reinstalling");
deletePlugin(embedded.key);
}
else if(existing != null && _isFirstEmbedUpdate)
Logger.i(TAG, "Embedded plugin [${existing.config.id}] ${existing.config.name}, up to date (${existing.config.version} >= ${embeddedConfig?.version})");
}
}
_isFirstEmbedUpdate = false;
}
fun installMissingEmbeddedPlugins(context: Context) {
val plugins = getPlugins();
@ -422,6 +421,13 @@ class StatePlugins {
}
@Serializable
private data class PluginConfig(
val SOURCES_EMBEDDED: Map<String, String>,
val SOURCES_EMBEDDED_DEFAULT: List<String>,
val SOURCES_UNDER_CONSTRUCTION: Map<String, String>
)
companion object {
private var _instance : StatePlugins? = null;
val instance : StatePlugins

View file

@ -67,7 +67,7 @@ class StateSubscriptions {
return Pair(_lastGlobalSubscriptionProgress, _lastGlobalSubscriptionTotal);
}
fun updateSubscriptionFeed(scope: CoroutineScope, onlyIfNull: Boolean = false, onProgress: ((Int, Int)->Unit)? = null) {
Logger.i(TAG, "updateSubscriptionFeed");
Logger.v(TAG, "updateSubscriptionFeed");
scope.launch(Dispatchers.IO) {
synchronized(_globalSubscriptionsLock) {
if (isGlobalUpdating || (onlyIfNull && _globalSubscriptionFeed != null)) {

View file

@ -67,7 +67,7 @@ class CommentViewHolder : ViewHolder {
processHandle.opinion(c.reference, Opinion.neutral);
}
StateApp.instance.scopeGetter().launch(Dispatchers.IO) {
StateApp.instance.scopeOrNull?.launch(Dispatchers.IO) {
try {
processHandle.fullyBackfillServers();
} catch (e: Throwable) {

View file

@ -78,8 +78,6 @@ class AnnouncementView : LinearLayout {
}
override fun onAttachedToWindow() {
Logger.i(TAG, "onAttachedToWindow");
super.onAttachedToWindow()
StateAnnouncement.instance.onAnnouncementChanged.subscribe(this) {
_scope?.launch(Dispatchers.Main) {
@ -91,14 +89,12 @@ class AnnouncementView : LinearLayout {
}
override fun onDetachedFromWindow() {
Logger.i(TAG, "onDetachedFromWindow");
super.onDetachedFromWindow()
StateAnnouncement.instance.onAnnouncementChanged.remove(this)
}
private fun refresh() {
Logger.i(TAG, "refresh");
Logger.v(TAG, "refresh");
val announcements = StateAnnouncement.instance.getVisibleAnnouncements(_category);
setAnnouncement(announcements.firstOrNull(), announcements.size);
}

View file

@ -107,7 +107,7 @@ class GestureControlView : LinearLayout {
} else {
val rx = p0.x / width;
val ry = p0.y / height;
Logger.i(TAG, "rx = $rx, ry = $ry, _isFullScreen = $_isFullScreen")
Logger.v(TAG, "rx = $rx, ry = $ry, _isFullScreen = $_isFullScreen")
if (ry > 0.1 && ry < 0.9) {
if (_isFullScreen && rx < 0.4) {
startAdjustingBrightness();

View file

@ -328,7 +328,7 @@ class FutoVideoPlayer : FutoVideoPlayerBase {
}
override fun onPlaybackStateChanged(playbackState: Int) {
Logger.i(TAG, "onPlaybackStateChanged $playbackState");
Logger.v(TAG, "onPlaybackStateChanged $playbackState");
val timeLeft = abs(position - duration);
if (playbackState == ExoPlayer.STATE_ENDED) {

View file

@ -130,9 +130,9 @@ abstract class FutoVideoPlayerBase : RelativeLayout {
override fun onAttachedToWindow() {
super.onAttachedToWindow();
Logger.i(TAG, "Attached onConnectionAvailable listener.");
Logger.v(TAG, "Attached onConnectionAvailable listener.");
StateApp.instance.onConnectionAvailable.subscribe(_referenceObject) {
Logger.i(TAG, "onConnectionAvailable");
Logger.v(TAG, "onConnectionAvailable");
val pos = position;
val dur = duration;

@ -1 +1 @@
Subproject commit 2061a75ec1a9428b83f8cef5b84738c6f9cac290
Subproject commit 75816961722eb8166866f96f7003d1f5e9d59e8e