Websocket fixes, onConcluded support

This commit is contained in:
Kelvin 2024-05-21 22:31:04 +02:00
parent 5f1c0209a8
commit aceab7b476
6 changed files with 82 additions and 15 deletions

View File

@ -7,4 +7,5 @@ interface IPlaybackTracker {
fun onInit(seconds: Double); fun onInit(seconds: Double);
fun onProgress(seconds: Double, isPlaying: Boolean); fun onProgress(seconds: Double, isPlaying: Boolean);
fun onConcluded();
} }

View File

@ -17,6 +17,8 @@ class JSPlaybackTracker: IPlaybackTracker {
private var _lastRequest: Long = Long.MIN_VALUE; private var _lastRequest: Long = Long.MIN_VALUE;
private val _hasOnConcluded: Boolean;
override var nextRequest: Int = 1000 override var nextRequest: Int = 1000
private set; private set;
@ -26,6 +28,7 @@ class JSPlaybackTracker: IPlaybackTracker {
throw ScriptImplementationException(config, "Missing onProgress on PlaybackTracker"); throw ScriptImplementationException(config, "Missing onProgress on PlaybackTracker");
if(!obj.has("nextRequest")) if(!obj.has("nextRequest"))
throw ScriptImplementationException(config, "Missing nextRequest on PlaybackTracker"); throw ScriptImplementationException(config, "Missing nextRequest on PlaybackTracker");
_hasOnConcluded = obj.has("onConcluded");
this._config = config; this._config = config;
this._obj = obj; this._obj = obj;
@ -59,6 +62,16 @@ class JSPlaybackTracker: IPlaybackTracker {
} }
} }
} }
override fun onConcluded() {
warnIfMainThread("JSPlaybackTracker.onConcluded");
if(_hasOnConcluded) {
synchronized(_obj) {
Logger.i("JSPlaybackTracker", "onConcluded");
_obj.invokeVoid("onConcluded", -1);
}
}
}
override fun shouldUpdate(): Boolean = (_lastRequest < 0 || (System.currentTimeMillis() - _lastRequest) > nextRequest); override fun shouldUpdate(): Boolean = (_lastRequest < 0 || (System.currentTimeMillis() - _lastRequest) > nextRequest);
} }

View File

@ -191,6 +191,8 @@ class PackageHttp: V8Package {
@Transient @Transient
private val _client: ManagedHttpClient; private val _client: ManagedHttpClient;
val parentConfig: IV8PluginConfig get() = _package._config;
@Transient @Transient
private val _defaultHeaders = mutableMapOf<String, String>(); private val _defaultHeaders = mutableMapOf<String, String>();
@Transient @Transient
@ -431,7 +433,7 @@ class PackageHttp: V8Package {
val hasClosed = socketObj.has("closed"); val hasClosed = socketObj.has("closed");
val hasFailure = socketObj.has("failure"); val hasFailure = socketObj.has("failure");
//socketObj.setWeak(); //We have to manage this lifecycle socketObj.setWeak(); //We have to manage this lifecycle
_listeners = socketObj; _listeners = socketObj;
_socket = _packageClient.logExceptions { _socket = _packageClient.logExceptions {
@ -440,8 +442,14 @@ class PackageHttp: V8Package {
override fun open() { override fun open() {
Logger.i(TAG, "Websocket opened: " + _url); Logger.i(TAG, "Websocket opened: " + _url);
_isOpen = true; _isOpen = true;
if(hasOpen) if(hasOpen) {
_listeners?.invokeVoid("open", arrayOf<Any>()); try {
_listeners?.invokeVoid("open", arrayOf<Any>());
}
catch(ex: Throwable){
Logger.e(TAG, "Socket for [${_packageClient.parentConfig.name}] open failed: " + ex.message, ex);
}
}
} }
override fun message(msg: String) { override fun message(msg: String) {
if(hasMessage) { if(hasMessage) {
@ -453,18 +461,37 @@ class PackageHttp: V8Package {
} }
override fun closing(code: Int, reason: String) { override fun closing(code: Int, reason: String) {
if(hasClosing) if(hasClosing)
_listeners?.invokeVoid("closing", code, reason); {
try {
_listeners?.invokeVoid("closing", code, reason);
}
catch(ex: Throwable){
Logger.e(TAG, "Socket for [${_packageClient.parentConfig.name}] closing failed: " + ex.message, ex);
}
}
} }
override fun closed(code: Int, reason: String) { override fun closed(code: Int, reason: String) {
_isOpen = false; _isOpen = false;
if(hasClosed) if(hasClosed) {
_listeners?.invokeVoid("closed", code, reason); try {
_listeners?.invokeVoid("closed", code, reason);
}
catch(ex: Throwable){
Logger.e(TAG, "Socket for [${_packageClient.parentConfig.name}] closed failed: " + ex.message, ex);
}
}
} }
override fun failure(exception: Throwable) { override fun failure(exception: Throwable) {
_isOpen = false; _isOpen = false;
Logger.e(TAG, "Websocket failure: ${exception.message} (${_url})", exception); Logger.e(TAG, "Websocket failure: ${exception.message} (${_url})", exception);
if(hasFailure) if(hasFailure) {
_listeners?.invokeVoid("failure", exception.message); try {
_listeners?.invokeVoid("failure", exception.message);
}
catch(ex: Throwable){
Logger.e(TAG, "Socket for [${_packageClient.parentConfig.name}] closed failed: " + ex.message, ex);
}
}
} }
}); });
}; };
@ -474,6 +501,16 @@ class PackageHttp: V8Package {
fun send(msg: String) { fun send(msg: String) {
_socket?.send(msg); _socket?.send(msg);
} }
@V8Function
fun close() {
_socket?.close(1000, "");
}
@V8Function
fun close(code: Int?, reason: String?) {
_socket?.close(code ?: 1000, reason ?: "");
_listeners?.close()
}
} }
data class RequestDescriptor( data class RequestDescriptor(

View File

@ -690,7 +690,7 @@ class VideoDetailView : ConstraintLayout {
_lastAudioSource = null; _lastAudioSource = null;
_lastSubtitleSource = null; _lastSubtitleSource = null;
video = null; video = null;
_playbackTracker = null; cleanupPlaybackTracker();
Logger.i(TAG, "Keep screen on unset onClose") Logger.i(TAG, "Keep screen on unset onClose")
fragment.activity?.window?.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); fragment.activity?.window?.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}; };
@ -1033,7 +1033,7 @@ class VideoDetailView : ConstraintLayout {
_searchVideo = null; _searchVideo = null;
video = null; video = null;
_playbackTracker = null; cleanupPlaybackTracker();
_url = url; _url = url;
_videoResumePositionMilliseconds = resumeSeconds * 1000; _videoResumePositionMilliseconds = resumeSeconds * 1000;
_rating.visibility = View.GONE; _rating.visibility = View.GONE;
@ -1071,7 +1071,7 @@ class VideoDetailView : ConstraintLayout {
} }
this.video = null; this.video = null;
this._playbackTracker = null; cleanupPlaybackTracker();
_searchVideo = video; _searchVideo = video;
_videoResumePositionMilliseconds = resumeSeconds * 1000; _videoResumePositionMilliseconds = resumeSeconds * 1000;
setLastPositionMilliseconds(_videoResumePositionMilliseconds, false); setLastPositionMilliseconds(_videoResumePositionMilliseconds, false);
@ -1206,7 +1206,7 @@ class VideoDetailView : ConstraintLayout {
} }
this.videoLocal = videoLocal; this.videoLocal = videoLocal;
this.video = video; this.video = video;
this._playbackTracker = null; cleanupPlaybackTracker();
if(video is JSVideoDetails) { if(video is JSVideoDetails) {
val me = this; val me = this;
@ -1522,6 +1522,22 @@ class VideoDetailView : ConstraintLayout {
} }
} }
fun cleanupPlaybackTracker(){
val tracker = _playbackTracker;
if(tracker != null) {
_playbackTracker = null;
fragment.lifecycleScope.launch(Dispatchers.IO) {
Logger.i(TAG, "Cleaning up old playback tracker");
try {
tracker.onConcluded();
}
catch(ex: Throwable) {
Logger.e(TAG, "Failed to cleanup playback tracker", ex);
}
}
}
}
//Source Loads //Source Loads
private fun loadCurrentVideo(resumePositionMs: Long = 0) { private fun loadCurrentVideo(resumePositionMs: Long = 0) {
_didStop = false; _didStop = false;
@ -2016,7 +2032,7 @@ class VideoDetailView : ConstraintLayout {
private fun fetchVideo() { private fun fetchVideo() {
Logger.i(TAG, "fetchVideo") Logger.i(TAG, "fetchVideo")
video = null; video = null;
_playbackTracker = null; cleanupPlaybackTracker();
val url = _url; val url = _url;
if (url != null && url.isNotBlank()) { if (url != null && url.isNotBlank()) {

@ -1 +1 @@
Subproject commit 59d2200f9220f2add3c4b7eccc314306503493a3 Subproject commit 611f692ced94bac637907b105170a4143580281a

@ -1 +1 @@
Subproject commit 59d2200f9220f2add3c4b7eccc314306503493a3 Subproject commit 611f692ced94bac637907b105170a4143580281a