mirror of
https://gitlab.futo.org/videostreaming/grayjay.git
synced 2025-08-03 06:40:19 +00:00
Cleaning up some logs, reducing retry intervals, planned live stream auto refresh, tweak some buffer timings, fixing some scopes
This commit is contained in:
parent
3a7e477e9b
commit
f76a5b5f01
27 changed files with 148 additions and 81 deletions
|
@ -333,7 +333,7 @@ class Settings : FragmentedStorageFileJson() {
|
||||||
"Submit logs to help us narrow down issues", 1
|
"Submit logs to help us narrow down issues", 1
|
||||||
)
|
)
|
||||||
fun submitLogs() {
|
fun submitLogs() {
|
||||||
StateApp.instance.scopeGetter().launch(Dispatchers.IO) {
|
StateApp.instance.scopeOrNull?.launch(Dispatchers.IO) {
|
||||||
try {
|
try {
|
||||||
if (!Logger.submitLogs()) {
|
if (!Logger.submitLogs()) {
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
|
@ -462,7 +462,7 @@ class Settings : FragmentedStorageFileJson() {
|
||||||
fun viewChangelog() {
|
fun viewChangelog() {
|
||||||
UIDialogs.toast("Retrieving changelog");
|
UIDialogs.toast("Retrieving changelog");
|
||||||
SettingsActivity.getActivity()?.let {
|
SettingsActivity.getActivity()?.let {
|
||||||
StateApp.instance.scopeGetter().launch(Dispatchers.IO) {
|
StateApp.instance.scopeOrNull?.launch(Dispatchers.IO) {
|
||||||
try {
|
try {
|
||||||
val version = StateUpdate.instance.downloadVersionCode(ManagedHttpClient()) ?: return@launch;
|
val version = StateUpdate.instance.downloadVersionCode(ManagedHttpClient()) ?: return@launch;
|
||||||
Logger.i(TAG, "Version retrieved $version");
|
Logger.i(TAG, "Version retrieved $version");
|
||||||
|
|
|
@ -392,7 +392,7 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
Logger.i(TAG, "onResume")
|
Logger.v(TAG, "onResume")
|
||||||
|
|
||||||
val curOrientation = _orientationManager.orientation;
|
val curOrientation = _orientationManager.orientation;
|
||||||
|
|
||||||
|
@ -408,13 +408,10 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
|
||||||
val videoToOpen = StateSaved.instance.videoToOpen;
|
val videoToOpen = StateSaved.instance.videoToOpen;
|
||||||
|
|
||||||
if (_wasStopped) {
|
if (_wasStopped) {
|
||||||
Logger.i(TAG, "_wasStopped is true");
|
|
||||||
Logger.i(TAG, "set _wasStopped = false");
|
|
||||||
_wasStopped = false;
|
_wasStopped = false;
|
||||||
|
|
||||||
Logger.i(TAG, "onResume videoToOpen=$videoToOpen");
|
|
||||||
|
|
||||||
if (videoToOpen != null && _fragVideoDetail.state == VideoDetailFragment.State.CLOSED) {
|
if (videoToOpen != null && _fragVideoDetail.state == VideoDetailFragment.State.CLOSED) {
|
||||||
|
Logger.i(TAG, "onResume videoToOpen=$videoToOpen");
|
||||||
if (StatePlatform.instance.hasEnabledVideoClient(videoToOpen.url)) {
|
if (StatePlatform.instance.hasEnabledVideoClient(videoToOpen.url)) {
|
||||||
navigate(_fragVideoDetail, UrlVideoWithTime(videoToOpen.url, videoToOpen.timeSeconds, false));
|
navigate(_fragVideoDetail, UrlVideoWithTime(videoToOpen.url, videoToOpen.timeSeconds, false));
|
||||||
_fragVideoDetail.maximizeVideoDetail(true);
|
_fragVideoDetail.maximizeVideoDetail(true);
|
||||||
|
@ -427,13 +424,13 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
|
||||||
|
|
||||||
override fun onPause() {
|
override fun onPause() {
|
||||||
super.onPause();
|
super.onPause();
|
||||||
Logger.i(TAG, "onPause")
|
Logger.v(TAG, "onPause")
|
||||||
_isVisible = false;
|
_isVisible = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStop() {
|
override fun onStop() {
|
||||||
super.onStop()
|
super.onStop()
|
||||||
Logger.i(TAG, "_wasStopped = true");
|
Logger.v(TAG, "_wasStopped = true");
|
||||||
_wasStopped = true;
|
_wasStopped = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -722,22 +719,20 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
|
||||||
}
|
}
|
||||||
_fragVideoDetail.onOrientationChanged(OrientationManager.Orientation.PORTRAIT);
|
_fragVideoDetail.onOrientationChanged(OrientationManager.Orientation.PORTRAIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.i(TAG, "onRestart5");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean, newConfig: Configuration) {
|
override fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean, newConfig: Configuration) {
|
||||||
super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig);
|
super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig);
|
||||||
|
|
||||||
val isStop: Boolean = lifecycle.currentState == Lifecycle.State.CREATED;
|
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);
|
_fragVideoDetail?.onPictureInPictureModeChanged(isInPictureInPictureMode, isStop, newConfig);
|
||||||
Logger.i(TAG, "onPictureInPictureModeChanged Ready");
|
Logger.v(TAG, "onPictureInPictureModeChanged Ready");
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
Logger.i(TAG, "onDestroy")
|
Logger.v(TAG, "onDestroy")
|
||||||
|
|
||||||
_orientationManager.disable();
|
_orientationManager.disable();
|
||||||
|
|
||||||
|
|
|
@ -63,7 +63,7 @@ class ManagedHttpServer(private val _requestedPort: Int = 0) {
|
||||||
}
|
}
|
||||||
}.start();
|
}.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
|
@Synchronized
|
||||||
fun stop() {
|
fun stop() {
|
||||||
|
|
|
@ -94,7 +94,10 @@ class LiveChatManager {
|
||||||
if(_pager is JSLiveEventPager)
|
if(_pager is JSLiveEventPager)
|
||||||
nextInterval = _pager.nextRequest.coerceAtLeast(800).toLong();
|
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) {
|
_scope.launch(Dispatchers.Main) {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -11,12 +11,14 @@ class PlatformClientPool {
|
||||||
private val _parent: JSClient;
|
private val _parent: JSClient;
|
||||||
private val _pool: HashMap<JSClient, Int> = hashMapOf();
|
private val _pool: HashMap<JSClient, Int> = hashMapOf();
|
||||||
private var _poolCounter = 0;
|
private var _poolCounter = 0;
|
||||||
|
private val _poolName: String?;
|
||||||
|
|
||||||
var isDead: Boolean = false
|
var isDead: Boolean = false
|
||||||
private set;
|
private set;
|
||||||
val onDead = Event2<JSClient, PlatformClientPool>();
|
val onDead = Event2<JSClient, PlatformClientPool>();
|
||||||
|
|
||||||
constructor(parentClient: IPlatformClient) {
|
constructor(parentClient: IPlatformClient, name: String? = null) {
|
||||||
|
_poolName = name;
|
||||||
if(parentClient !is JSClient)
|
if(parentClient !is JSClient)
|
||||||
throw IllegalArgumentException("Pooling only supported for JSClients right now");
|
throw IllegalArgumentException("Pooling only supported for JSClients right now");
|
||||||
Logger.i(TAG, "Pool for ${parentClient.name} was started");
|
Logger.i(TAG, "Pool for ${parentClient.name} was started");
|
||||||
|
@ -47,7 +49,7 @@ class PlatformClientPool {
|
||||||
_poolCounter++;
|
_poolCounter++;
|
||||||
reserved = _pool.keys.find { !it.isBusy };
|
reserved = _pool.keys.find { !it.isBusy };
|
||||||
if(reserved == null && _pool.size < capacity) {
|
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 = _parent.getCopy();
|
||||||
reserved?.initialize();
|
reserved?.initialize();
|
||||||
_pool[reserved!!] = _poolCounter;
|
_pool[reserved!!] = _poolCounter;
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
package com.futo.platformplayer.api.media
|
package com.futo.platformplayer.api.media
|
||||||
|
|
||||||
class PlatformMultiClientPool {
|
class PlatformMultiClientPool {
|
||||||
|
private val _name: String;
|
||||||
private val _maxCap: Int;
|
private val _maxCap: Int;
|
||||||
private val _clientPools: HashMap<IPlatformClient, PlatformClientPool> = hashMapOf();
|
private val _clientPools: HashMap<IPlatformClient, PlatformClientPool> = hashMapOf();
|
||||||
|
|
||||||
private var _isFake = false;
|
private var _isFake = false;
|
||||||
|
|
||||||
constructor(maxCap: Int = -1) {
|
constructor(name: String, maxCap: Int = -1) {
|
||||||
|
_name = name;
|
||||||
_maxCap = if(maxCap > 0)
|
_maxCap = if(maxCap > 0)
|
||||||
maxCap
|
maxCap
|
||||||
else 99;
|
else 99;
|
||||||
|
@ -17,7 +19,7 @@ class PlatformMultiClientPool {
|
||||||
return parentClient;
|
return parentClient;
|
||||||
val pool = synchronized(_clientPools) {
|
val pool = synchronized(_clientPools) {
|
||||||
if(!_clientPools.containsKey(parentClient))
|
if(!_clientPools.containsKey(parentClient))
|
||||||
_clientPools[parentClient] = PlatformClientPool(parentClient).apply {
|
_clientPools[parentClient] = PlatformClientPool(parentClient, _name).apply {
|
||||||
this.onDead.subscribe { client, pool ->
|
this.onDead.subscribe { client, pool ->
|
||||||
synchronized(_clientPools) {
|
synchronized(_clientPools) {
|
||||||
if(_clientPools[parentClient] == pool)
|
if(_clientPools[parentClient] == pool)
|
||||||
|
|
|
@ -42,7 +42,6 @@ open class JSContent : IPlatformContent, IPluginSourced {
|
||||||
|
|
||||||
id = PlatformID.fromV8(_pluginConfig, _content.getOrThrow(config, "id", contextName));
|
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();
|
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));
|
author = PlatformAuthorLink.fromV8(_pluginConfig, _content.getOrThrow(config, "author", contextName));
|
||||||
|
|
||||||
val datetimeInt = _content.getOrThrow<Int>(config, "datetime", contextName).toLong();
|
val datetimeInt = _content.getOrThrow<Int>(config, "datetime", contextName).toLong();
|
||||||
|
|
|
@ -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 {
|
val timeAwait = measureTimeMillis {
|
||||||
_currentResults = results.map { it.await() }.mapNotNull { it };
|
_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;
|
_currentResultExceptions = exceptions;
|
||||||
return _currentResults;
|
return _currentResults;
|
||||||
|
|
|
@ -111,7 +111,7 @@ class ChannelContentCache {
|
||||||
init {
|
init {
|
||||||
val results = pager.getResults();
|
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) {
|
scope.launch(Dispatchers.IO) {
|
||||||
try {
|
try {
|
||||||
val newCacheItems = instance.cacheVideos(results);
|
val newCacheItems = instance.cacheVideos(results);
|
||||||
|
|
|
@ -64,7 +64,7 @@ class StateCasting {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun serviceResolved(event: ServiceEvent) {
|
override fun serviceResolved(event: ServiceEvent) {
|
||||||
Logger.i(TAG, "ChromeCast service resolved: " + event.info);
|
Logger.v(TAG, "ChromeCast service resolved: " + event.info);
|
||||||
addOrUpdateDevice(event);
|
addOrUpdateDevice(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -91,7 +91,7 @@ class TaskHandler<TParameter, TResult> {
|
||||||
if (!onError.emit(e, parameter)) {
|
if (!onError.emit(e, parameter)) {
|
||||||
Logger.e(TAG, "Uncaught exception handled by TaskHandler.", e);
|
Logger.e(TAG, "Uncaught exception handled by TaskHandler.", e);
|
||||||
} else {
|
} else {
|
||||||
Logger.w(TAG, "Handled exception in TaskHandler invoke.", e);
|
//Logger.w(TAG, "Handled exception in TaskHandler invoke.", e); (Prevents duplicate logs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,7 @@ class CommentDialog(context: Context?, val contextUrl: String, val ref: Protocol
|
||||||
val processHandle = StatePolycentric.instance.processHandle!!
|
val processHandle = StatePolycentric.instance.processHandle!!
|
||||||
val eventPointer = processHandle.post(comment, null, ref)
|
val eventPointer = processHandle.post(comment, null, ref)
|
||||||
|
|
||||||
StateApp.instance.scopeGetter().launch(Dispatchers.IO) {
|
StateApp.instance.scopeOrNull?.launch(Dispatchers.IO) {
|
||||||
try {
|
try {
|
||||||
processHandle.fullyBackfillServers()
|
processHandle.fullyBackfillServers()
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
|
|
|
@ -355,7 +355,7 @@ class PostDetailFragment : MainFragment {
|
||||||
processHandle.opinion(ref, Opinion.neutral);
|
processHandle.opinion(ref, Opinion.neutral);
|
||||||
}
|
}
|
||||||
|
|
||||||
StateApp.instance.scopeGetter().launch(Dispatchers.IO) {
|
StateApp.instance.scopeOrNull?.launch(Dispatchers.IO) {
|
||||||
try {
|
try {
|
||||||
processHandle.fullyBackfillServers();
|
processHandle.fullyBackfillServers();
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
|
|
|
@ -306,12 +306,12 @@ class VideoDetailFragment : MainFragment {
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
Logger.i(TAG, "onResume");
|
Logger.v(TAG, "onResume");
|
||||||
_isActive = true;
|
_isActive = true;
|
||||||
_leavingPiP = false;
|
_leavingPiP = false;
|
||||||
|
|
||||||
_viewDetail?.let {
|
_viewDetail?.let {
|
||||||
Logger.i(TAG, "onResume preventPictureInPicture=false");
|
Logger.v(TAG, "onResume preventPictureInPicture=false");
|
||||||
it.preventPictureInPicture = false;
|
it.preventPictureInPicture = false;
|
||||||
|
|
||||||
if (state != State.CLOSED) {
|
if (state != State.CLOSED) {
|
||||||
|
@ -326,7 +326,7 @@ class VideoDetailFragment : MainFragment {
|
||||||
}
|
}
|
||||||
override fun onPause() {
|
override fun onPause() {
|
||||||
super.onPause();
|
super.onPause();
|
||||||
Logger.i(TAG, "onPause");
|
Logger.v(TAG, "onPause");
|
||||||
_isActive = false;
|
_isActive = false;
|
||||||
|
|
||||||
if(!isInPictureInPicture && state != State.CLOSED)
|
if(!isInPictureInPicture && state != State.CLOSED)
|
||||||
|
@ -334,7 +334,7 @@ class VideoDetailFragment : MainFragment {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStop() {
|
override fun onStop() {
|
||||||
Logger.i(TAG, "onStop");
|
Logger.v(TAG, "onStop");
|
||||||
|
|
||||||
stopIfRequired();
|
stopIfRequired();
|
||||||
super.onStop();
|
super.onStop();
|
||||||
|
@ -352,7 +352,7 @@ class VideoDetailFragment : MainFragment {
|
||||||
shouldStop = false;
|
shouldStop = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.i(TAG, "shouldStop: $shouldStop");
|
Logger.v(TAG, "shouldStop: $shouldStop");
|
||||||
if(shouldStop) {
|
if(shouldStop) {
|
||||||
_viewDetail?.let {
|
_viewDetail?.let {
|
||||||
val v = it.video ?: return@let;
|
val v = it.video ?: return@let;
|
||||||
|
@ -361,13 +361,13 @@ class VideoDetailFragment : MainFragment {
|
||||||
|
|
||||||
_viewDetail?.onStop();
|
_viewDetail?.onStop();
|
||||||
StateCasting.instance.onStop();
|
StateCasting.instance.onStop();
|
||||||
Logger.i(TAG, "called onStop() shouldStop: $shouldStop");
|
Logger.v(TAG, "called onStop() shouldStop: $shouldStop");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroyMainView() {
|
override fun onDestroyMainView() {
|
||||||
super.onDestroyMainView();
|
super.onDestroyMainView();
|
||||||
Logger.i(TAG, "onDestroyMainView");
|
Logger.v(TAG, "onDestroyMainView");
|
||||||
_viewDetail?.let {
|
_viewDetail?.let {
|
||||||
_viewDetail = null;
|
_viewDetail = null;
|
||||||
it.onDestroy();
|
it.onDestroy();
|
||||||
|
|
|
@ -100,6 +100,7 @@ import kotlinx.coroutines.*
|
||||||
import userpackage.Protocol
|
import userpackage.Protocol
|
||||||
import java.time.OffsetDateTime
|
import java.time.OffsetDateTime
|
||||||
import kotlin.collections.ArrayList
|
import kotlin.collections.ArrayList
|
||||||
|
import kotlin.math.abs
|
||||||
import kotlin.math.roundToLong
|
import kotlin.math.roundToLong
|
||||||
import kotlin.streams.toList
|
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_5 = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 5f, resources.displayMetrics);
|
||||||
private val DP_2 = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 2f, resources.displayMetrics);
|
private val DP_2 = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 2f, resources.displayMetrics);
|
||||||
|
|
||||||
private var _retryJob: Job? = null;
|
private var _retryJob: Job? = null;
|
||||||
private var _retryCount = 0;
|
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) {
|
constructor(context: Context, attrs : AttributeSet? = null) : super(context, attrs) {
|
||||||
inflate(context, R.layout.fragview_video_detail, this);
|
inflate(context, R.layout.fragview_video_detail, this);
|
||||||
|
@ -660,7 +671,7 @@ class VideoDetailView : ConstraintLayout {
|
||||||
|
|
||||||
//Lifecycle
|
//Lifecycle
|
||||||
fun onResume() {
|
fun onResume() {
|
||||||
Logger.i(TAG, "onResume");
|
Logger.v(TAG, "onResume");
|
||||||
_onPauseCalled = false;
|
_onPauseCalled = false;
|
||||||
|
|
||||||
Logger.i(TAG, "_video: ${video?.name ?: "no video"}");
|
Logger.i(TAG, "_video: ${video?.name ?: "no video"}");
|
||||||
|
@ -694,7 +705,7 @@ class VideoDetailView : ConstraintLayout {
|
||||||
_player.updateRotateLock();
|
_player.updateRotateLock();
|
||||||
}
|
}
|
||||||
fun onPause() {
|
fun onPause() {
|
||||||
Logger.i(TAG, "onPause");
|
Logger.v(TAG, "onPause");
|
||||||
|
|
||||||
_onPauseCalled = true;
|
_onPauseCalled = true;
|
||||||
_taskLoadVideo.cancel();
|
_taskLoadVideo.cancel();
|
||||||
|
@ -722,6 +733,8 @@ class VideoDetailView : ConstraintLayout {
|
||||||
_overlay_quality_selector?.hide();
|
_overlay_quality_selector?.hide();
|
||||||
_retryJob?.cancel();
|
_retryJob?.cancel();
|
||||||
_retryJob = null;
|
_retryJob = null;
|
||||||
|
_liveTryJob?.cancel();
|
||||||
|
_liveTryJob = null;
|
||||||
_taskLoadVideo.cancel();
|
_taskLoadVideo.cancel();
|
||||||
handleStop();
|
handleStop();
|
||||||
_didStop = true;
|
_didStop = true;
|
||||||
|
@ -808,6 +821,8 @@ class VideoDetailView : ConstraintLayout {
|
||||||
|
|
||||||
_retryJob?.cancel();
|
_retryJob?.cancel();
|
||||||
_retryJob = null;
|
_retryJob = null;
|
||||||
|
_liveTryJob?.cancel();
|
||||||
|
_liveTryJob = null;
|
||||||
_retryCount = 0;
|
_retryCount = 0;
|
||||||
fetchVideo();
|
fetchVideo();
|
||||||
|
|
||||||
|
@ -897,6 +912,8 @@ class VideoDetailView : ConstraintLayout {
|
||||||
|
|
||||||
_retryJob?.cancel();
|
_retryJob?.cancel();
|
||||||
_retryJob = null;
|
_retryJob = null;
|
||||||
|
_liveTryJob?.cancel();
|
||||||
|
_liveTryJob = null;
|
||||||
_retryCount = 0;
|
_retryCount = 0;
|
||||||
fetchVideo();
|
fetchVideo();
|
||||||
}
|
}
|
||||||
|
@ -1034,7 +1051,7 @@ class VideoDetailView : ConstraintLayout {
|
||||||
processHandle.opinion(ref, Opinion.neutral);
|
processHandle.opinion(ref, Opinion.neutral);
|
||||||
}
|
}
|
||||||
|
|
||||||
StateApp.instance.scopeGetter().launch(Dispatchers.IO) {
|
fragment.lifecycleScope.launch(Dispatchers.IO) {
|
||||||
try {
|
try {
|
||||||
processHandle.fullyBackfillServers();
|
processHandle.fullyBackfillServers();
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
|
@ -1130,6 +1147,8 @@ class VideoDetailView : ConstraintLayout {
|
||||||
if(video.isLive && video.live != null) {
|
if(video.isLive && video.live != null) {
|
||||||
loadLiveChat(video);
|
loadLiveChat(video);
|
||||||
}
|
}
|
||||||
|
if(video.isLive && video.live == null && !video.video.videoSources.any())
|
||||||
|
startLiveTry(video);
|
||||||
|
|
||||||
updateMoreButtons();
|
updateMoreButtons();
|
||||||
}
|
}
|
||||||
|
@ -1259,7 +1278,7 @@ class VideoDetailView : ConstraintLayout {
|
||||||
//If LiveStream, set to end
|
//If LiveStream, set to end
|
||||||
if(videoSource is IDashManifestSource || videoSource is IHLSManifestSource) {
|
if(videoSource is IDashManifestSource || videoSource is IHLSManifestSource) {
|
||||||
if (video?.isLive == true) {
|
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 }
|
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?) {
|
fun setProgressBarOverlayed(isOverlayed: Boolean?) {
|
||||||
Logger.i(TAG, "setProgressBarOverlayed(isOverlayed: ${isOverlayed ?: "null"})");
|
Logger.v(TAG, "setProgressBarOverlayed(isOverlayed: ${isOverlayed ?: "null"})");
|
||||||
isOverlayed?.let{ _cast.setProgressBarOverlayed(it) };
|
isOverlayed?.let{ _cast.setProgressBarOverlayed(it) };
|
||||||
|
|
||||||
if(isOverlayed == null) {
|
if(isOverlayed == null) {
|
||||||
|
@ -2080,6 +2099,8 @@ class VideoDetailView : ConstraintLayout {
|
||||||
_retryCount = 0;
|
_retryCount = 0;
|
||||||
_retryJob?.cancel();
|
_retryJob?.cancel();
|
||||||
_retryJob = null;
|
_retryJob = null;
|
||||||
|
_liveTryJob?.cancel();
|
||||||
|
_liveTryJob = null;
|
||||||
UIDialogs.showGeneralRetryErrorDialog(context, "Failed to load video (ScriptException)", it, ::fetchVideo);
|
UIDialogs.showGeneralRetryErrorDialog(context, "Failed to load video (ScriptException)", it, ::fetchVideo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2090,6 +2111,8 @@ class VideoDetailView : ConstraintLayout {
|
||||||
_retryCount = 0;
|
_retryCount = 0;
|
||||||
_retryJob?.cancel();
|
_retryJob?.cancel();
|
||||||
_retryJob = null;
|
_retryJob = null;
|
||||||
|
_liveTryJob?.cancel();
|
||||||
|
_liveTryJob = null;
|
||||||
UIDialogs.showGeneralRetryErrorDialog(context, "Failed to load video", it, ::fetchVideo);
|
UIDialogs.showGeneralRetryErrorDialog(context, "Failed to load video", it, ::fetchVideo);
|
||||||
}
|
}
|
||||||
} else TaskHandler(IPlatformVideoDetails::class.java, {fragment.lifecycleScope});
|
} else TaskHandler(IPlatformVideoDetails::class.java, {fragment.lifecycleScope});
|
||||||
|
@ -2107,14 +2130,16 @@ class VideoDetailView : ConstraintLayout {
|
||||||
Log.i(TAG, "handleErrorOrCall _retryCount=$_retryCount, starting retry job");
|
Log.i(TAG, "handleErrorOrCall _retryCount=$_retryCount, starting retry job");
|
||||||
|
|
||||||
_retryJob?.cancel();
|
_retryJob?.cancel();
|
||||||
_retryJob = StateApp.instance.scopeGetter().launch(Dispatchers.Main) {
|
_retryJob = StateApp.instance.scopeOrNull?.launch(Dispatchers.Main) {
|
||||||
try {
|
try {
|
||||||
delay(_retryIntervals[_retryCount++] * 1000);
|
delay(_retryIntervals[_retryCount++] * 1000);
|
||||||
fetchVideo();
|
fetchVideo();
|
||||||
} catch (e: Throwable) {
|
} 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()) {
|
} else if (isConnected && nextVideo()) {
|
||||||
Log.i(TAG, "handleErrorOrCall retries failed, is connected, skipped to next video");
|
Log.i(TAG, "handleErrorOrCall retries failed, is connected, skipped to next video");
|
||||||
} else {
|
} 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) {
|
fun applyFragment(frag: VideoDetailFragment) {
|
||||||
fragment = frag;
|
fragment = frag;
|
||||||
fragment.onMinimize.subscribe {
|
fragment.onMinimize.subscribe {
|
||||||
|
|
|
@ -50,7 +50,7 @@ class MediaPlaybackService : Service() {
|
||||||
private var _focusRequest: AudioFocusRequest? = null;
|
private var _focusRequest: AudioFocusRequest? = null;
|
||||||
|
|
||||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||||
Logger.i(TAG, "onStartCommand");
|
Logger.v(TAG, "onStartCommand");
|
||||||
|
|
||||||
|
|
||||||
if(!FragmentedStorage.isInitialized) {
|
if(!FragmentedStorage.isInitialized) {
|
||||||
|
@ -122,12 +122,12 @@ class MediaPlaybackService : Service() {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
Logger.i(TAG, "onCreate called");
|
Logger.v(TAG, "onCreate");
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
Logger.i(TAG, "onDestroy called");
|
Logger.v(TAG, "onDestroy");
|
||||||
_instance = null;
|
_instance = null;
|
||||||
MediaControlReceiver.onCloseReceived.emit();
|
MediaControlReceiver.onCloseReceived.emit();
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
|
@ -138,7 +138,7 @@ class MediaPlaybackService : Service() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun closeMediaSession() {
|
fun closeMediaSession() {
|
||||||
Logger.i(TAG, "closeMediaSession called");
|
Logger.v(TAG, "closeMediaSession");
|
||||||
stopForeground(true);
|
stopForeground(true);
|
||||||
|
|
||||||
val focusRequest = _focusRequest;
|
val focusRequest = _focusRequest;
|
||||||
|
@ -159,7 +159,7 @@ class MediaPlaybackService : Service() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateMediaSession(videoUpdated: IPlatformVideo?) {
|
fun updateMediaSession(videoUpdated: IPlatformVideo?) {
|
||||||
Logger.i(TAG, "updateMediaSession called");
|
Logger.v(TAG, "updateMediaSession");
|
||||||
var isUpdating = false;
|
var isUpdating = false;
|
||||||
val video: IPlatformVideo;
|
val video: IPlatformVideo;
|
||||||
if(videoUpdated == null) {
|
if(videoUpdated == null) {
|
||||||
|
@ -270,7 +270,7 @@ class MediaPlaybackService : Service() {
|
||||||
val notif = builder.build();
|
val notif = builder.build();
|
||||||
notif.flags = notif.flags or NotificationCompat.FLAG_ONGOING_EVENT or NotificationCompat.FLAG_NO_CLEAR;
|
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);
|
startForeground(MEDIA_NOTIF_ID, notif, ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK);
|
||||||
|
|
||||||
|
|
|
@ -299,7 +299,7 @@ class StateApp {
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.onLogSubmitted.subscribe {
|
Logger.onLogSubmitted.subscribe {
|
||||||
scopeGetter().launch(Dispatchers.Main) {
|
scopeOrNull?.launch(Dispatchers.Main) {
|
||||||
try {
|
try {
|
||||||
if (it != null) {
|
if (it != null) {
|
||||||
UIDialogs.toast("Uploaded " + (it ?: "null"), true);
|
UIDialogs.toast("Uploaded " + (it ?: "null"), true);
|
||||||
|
@ -510,7 +510,6 @@ class StateApp {
|
||||||
if (_lastNetworkState != NetworkState.DISCONNECTED) {
|
if (_lastNetworkState != NetworkState.DISCONNECTED) {
|
||||||
scopeOrNull?.launch(Dispatchers.Main) {
|
scopeOrNull?.launch(Dispatchers.Main) {
|
||||||
try {
|
try {
|
||||||
Logger.i(TAG, "onConnectionAvailable emitted");
|
|
||||||
onConnectionAvailable.emit();
|
onConnectionAvailable.emit();
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
Logger.e(TAG, "Failed to emit onConnectionAvailable", e)
|
Logger.e(TAG, "Failed to emit onConnectionAvailable", e)
|
||||||
|
|
|
@ -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.
|
//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.
|
//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.
|
//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 _mainClientPool = PlatformMultiClientPool("Main", 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 _pagerClientPool = PlatformMultiClientPool("Pagers", 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 _channelClientPool = PlatformMultiClientPool("Channels", 15); //Used primarily for subscription/background channel fetches
|
||||||
private val _trackerClientPool = PlatformMultiClientPool(1); //Used exclusively for playback trackers
|
private val _trackerClientPool = PlatformMultiClientPool("Trackers", 1); //Used exclusively for playback trackers
|
||||||
private val _liveEventClientPool = PlatformMultiClientPool(1); //Used exclusively for live events
|
private val _liveEventClientPool = PlatformMultiClientPool("LiveEvents", 1); //Used exclusively for live events
|
||||||
|
|
||||||
|
|
||||||
private val _primaryClientPersistent = FragmentedStorage.get<StringStorage>("primaryClient");
|
private val _primaryClientPersistent = FragmentedStorage.get<StringStorage>("primaryClient");
|
||||||
|
|
|
@ -25,7 +25,7 @@ class StatePlayer {
|
||||||
private val MIN_BUFFER_DURATION = 10000;
|
private val MIN_BUFFER_DURATION = 10000;
|
||||||
private val MAX_BUFFER_DURATION = 60000;
|
private val MAX_BUFFER_DURATION = 60000;
|
||||||
private val MIN_PLAYBACK_START_BUFFER = 500;
|
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;
|
private val BUFFER_SIZE = 1024 * 64;
|
||||||
|
|
||||||
var isOpen : Boolean = false
|
var isOpen : Boolean = false
|
||||||
|
|
|
@ -27,12 +27,20 @@ class StatePlugins {
|
||||||
private val TAG = "StatePlugins";
|
private val TAG = "StatePlugins";
|
||||||
|
|
||||||
private val FORCE_REINSTALL_EMBEDDED = false;
|
private val FORCE_REINSTALL_EMBEDDED = false;
|
||||||
|
private var _isFirstEmbedUpdate = true;
|
||||||
|
|
||||||
private val _pluginScripts = FragmentedStorage.getDirectory<PluginScriptsDirectory>();
|
private val _pluginScripts = FragmentedStorage.getDirectory<PluginScriptsDirectory>();
|
||||||
private var _plugins = FragmentedStorage.storeJson<SourcePluginDescriptor>("plugins")
|
private var _plugins = FragmentedStorage.storeJson<SourcePluginDescriptor>("plugins")
|
||||||
.load();
|
.load();
|
||||||
private val iconsDir = FragmentedStorage.getDirectory<PluginIconStorage>();
|
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? {
|
fun getPluginIconOrNull(id: String): ImageVariable? {
|
||||||
if(iconsDir.hasIcon(id))
|
if(iconsDir.hasIcon(id))
|
||||||
return iconsDir.getIconBinary(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) {
|
private fun ensureSourcesConfigLoaded(context: Context) {
|
||||||
if (_embeddedSources != null && _embeddedSourcesDefault != null && _sourcesUnderConstruction != null) {
|
if (_embeddedSources != null && _embeddedSourcesDefault != null && _sourcesUnderConstruction != null) {
|
||||||
return
|
return
|
||||||
|
@ -122,8 +118,11 @@ class StatePlugins {
|
||||||
Logger.i(TAG, "Found outdated embedded plugin [${existing.config.id}] ${existing.config.name}, deleting and reinstalling");
|
Logger.i(TAG, "Found outdated embedded plugin [${existing.config.id}] ${existing.config.name}, deleting and reinstalling");
|
||||||
deletePlugin(embedded.key);
|
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) {
|
fun installMissingEmbeddedPlugins(context: Context) {
|
||||||
val plugins = getPlugins();
|
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 {
|
companion object {
|
||||||
private var _instance : StatePlugins? = null;
|
private var _instance : StatePlugins? = null;
|
||||||
val instance : StatePlugins
|
val instance : StatePlugins
|
||||||
|
|
|
@ -67,7 +67,7 @@ class StateSubscriptions {
|
||||||
return Pair(_lastGlobalSubscriptionProgress, _lastGlobalSubscriptionTotal);
|
return Pair(_lastGlobalSubscriptionProgress, _lastGlobalSubscriptionTotal);
|
||||||
}
|
}
|
||||||
fun updateSubscriptionFeed(scope: CoroutineScope, onlyIfNull: Boolean = false, onProgress: ((Int, Int)->Unit)? = null) {
|
fun updateSubscriptionFeed(scope: CoroutineScope, onlyIfNull: Boolean = false, onProgress: ((Int, Int)->Unit)? = null) {
|
||||||
Logger.i(TAG, "updateSubscriptionFeed");
|
Logger.v(TAG, "updateSubscriptionFeed");
|
||||||
scope.launch(Dispatchers.IO) {
|
scope.launch(Dispatchers.IO) {
|
||||||
synchronized(_globalSubscriptionsLock) {
|
synchronized(_globalSubscriptionsLock) {
|
||||||
if (isGlobalUpdating || (onlyIfNull && _globalSubscriptionFeed != null)) {
|
if (isGlobalUpdating || (onlyIfNull && _globalSubscriptionFeed != null)) {
|
||||||
|
|
|
@ -67,7 +67,7 @@ class CommentViewHolder : ViewHolder {
|
||||||
processHandle.opinion(c.reference, Opinion.neutral);
|
processHandle.opinion(c.reference, Opinion.neutral);
|
||||||
}
|
}
|
||||||
|
|
||||||
StateApp.instance.scopeGetter().launch(Dispatchers.IO) {
|
StateApp.instance.scopeOrNull?.launch(Dispatchers.IO) {
|
||||||
try {
|
try {
|
||||||
processHandle.fullyBackfillServers();
|
processHandle.fullyBackfillServers();
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
|
|
|
@ -78,8 +78,6 @@ class AnnouncementView : LinearLayout {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onAttachedToWindow() {
|
override fun onAttachedToWindow() {
|
||||||
Logger.i(TAG, "onAttachedToWindow");
|
|
||||||
|
|
||||||
super.onAttachedToWindow()
|
super.onAttachedToWindow()
|
||||||
StateAnnouncement.instance.onAnnouncementChanged.subscribe(this) {
|
StateAnnouncement.instance.onAnnouncementChanged.subscribe(this) {
|
||||||
_scope?.launch(Dispatchers.Main) {
|
_scope?.launch(Dispatchers.Main) {
|
||||||
|
@ -91,14 +89,12 @@ class AnnouncementView : LinearLayout {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDetachedFromWindow() {
|
override fun onDetachedFromWindow() {
|
||||||
Logger.i(TAG, "onDetachedFromWindow");
|
|
||||||
|
|
||||||
super.onDetachedFromWindow()
|
super.onDetachedFromWindow()
|
||||||
StateAnnouncement.instance.onAnnouncementChanged.remove(this)
|
StateAnnouncement.instance.onAnnouncementChanged.remove(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun refresh() {
|
private fun refresh() {
|
||||||
Logger.i(TAG, "refresh");
|
Logger.v(TAG, "refresh");
|
||||||
val announcements = StateAnnouncement.instance.getVisibleAnnouncements(_category);
|
val announcements = StateAnnouncement.instance.getVisibleAnnouncements(_category);
|
||||||
setAnnouncement(announcements.firstOrNull(), announcements.size);
|
setAnnouncement(announcements.firstOrNull(), announcements.size);
|
||||||
}
|
}
|
||||||
|
|
|
@ -107,7 +107,7 @@ class GestureControlView : LinearLayout {
|
||||||
} else {
|
} else {
|
||||||
val rx = p0.x / width;
|
val rx = p0.x / width;
|
||||||
val ry = p0.y / height;
|
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 (ry > 0.1 && ry < 0.9) {
|
||||||
if (_isFullScreen && rx < 0.4) {
|
if (_isFullScreen && rx < 0.4) {
|
||||||
startAdjustingBrightness();
|
startAdjustingBrightness();
|
||||||
|
|
|
@ -328,7 +328,7 @@ class FutoVideoPlayer : FutoVideoPlayerBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPlaybackStateChanged(playbackState: Int) {
|
override fun onPlaybackStateChanged(playbackState: Int) {
|
||||||
Logger.i(TAG, "onPlaybackStateChanged $playbackState");
|
Logger.v(TAG, "onPlaybackStateChanged $playbackState");
|
||||||
val timeLeft = abs(position - duration);
|
val timeLeft = abs(position - duration);
|
||||||
|
|
||||||
if (playbackState == ExoPlayer.STATE_ENDED) {
|
if (playbackState == ExoPlayer.STATE_ENDED) {
|
||||||
|
|
|
@ -130,9 +130,9 @@ abstract class FutoVideoPlayerBase : RelativeLayout {
|
||||||
override fun onAttachedToWindow() {
|
override fun onAttachedToWindow() {
|
||||||
super.onAttachedToWindow();
|
super.onAttachedToWindow();
|
||||||
|
|
||||||
Logger.i(TAG, "Attached onConnectionAvailable listener.");
|
Logger.v(TAG, "Attached onConnectionAvailable listener.");
|
||||||
StateApp.instance.onConnectionAvailable.subscribe(_referenceObject) {
|
StateApp.instance.onConnectionAvailable.subscribe(_referenceObject) {
|
||||||
Logger.i(TAG, "onConnectionAvailable");
|
Logger.v(TAG, "onConnectionAvailable");
|
||||||
|
|
||||||
val pos = position;
|
val pos = position;
|
||||||
val dur = duration;
|
val dur = duration;
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 2061a75ec1a9428b83f8cef5b84738c6f9cac290
|
Subproject commit 75816961722eb8166866f96f7003d1f5e9d59e8e
|
Loading…
Add table
Add a link
Reference in a new issue