casting: log exceptions from playback controls

This commit is contained in:
Marcus Hanestad 2025-09-04 13:08:40 +02:00
commit 3de5cd92ae
6 changed files with 94 additions and 103 deletions

View file

@ -128,33 +128,15 @@ class ExpCastingDevice(val device: RsCastingDevice) : CastingDevice() {
override val onSpeedChanged: Event1<Double>
get() = eventHandler.onSpeedChanged
override fun resumePlayback() = try {
device.resumePlayback()
} catch (_: Throwable) {
}
override fun pausePlayback() = try {
device.pausePlayback()
} catch (_: Throwable) {
}
override fun stopPlayback() = try {
device.stopPlayback()
} catch (_: Throwable) {
}
override fun seekTo(timeSeconds: Double) = try {
device.seek(timeSeconds)
} catch (_: Throwable) {
}
override fun resumePlayback() = device.resumePlayback()
override fun pausePlayback() = device.pausePlayback()
override fun stopPlayback() = device.stopPlayback()
override fun seekTo(timeSeconds: Double) = device.seek(timeSeconds)
override fun changeVolume(newVolume: Double) {
device.changeVolume(newVolume)
volume = newVolume
}
override fun changeSpeed(speed: Double) = device.changeSpeed(speed)
override fun connect() = device.connect(
ApplicationInfo(
"Grayjay Android",
@ -192,19 +174,16 @@ class ExpCastingDevice(val device: RsCastingDevice) : CastingDevice() {
duration: Double,
speed: Double?,
metadata: Metadata?
) = try {
device.load(
LoadRequest.Video(
contentType = contentType,
url = contentId,
resumePosition = resumePosition,
speed = speed,
volume = volume,
metadata = metadata
)
) = device.load(
LoadRequest.Video(
contentType = contentType,
url = contentId,
resumePosition = resumePosition,
speed = speed,
volume = volume,
metadata = metadata
)
} catch (_: Throwable) {
}
)
override fun loadContent(
contentType: String,
@ -213,19 +192,16 @@ class ExpCastingDevice(val device: RsCastingDevice) : CastingDevice() {
duration: Double,
speed: Double?,
metadata: Metadata?
) = try {
device.load(
LoadRequest.Content(
contentType = contentType,
content = content,
resumePosition = resumePosition,
speed = speed,
volume = volume,
metadata = metadata,
)
) = device.load(
LoadRequest.Content(
contentType = contentType,
content = content,
resumePosition = resumePosition,
speed = speed,
volume = volume,
metadata = metadata,
)
} catch (_: Throwable) {
}
)
override var connectionState = CastConnectionState.DISCONNECTED
override val protocolType: CastProtocolType

View file

@ -45,6 +45,8 @@ import com.futo.platformplayer.states.StateApp
import com.futo.platformplayer.stores.CastingDeviceInfoStorage
import com.futo.platformplayer.stores.FragmentedStorage
import com.futo.platformplayer.toUrlAddress
import com.futo.platformplayer.views.casting.CastView
import com.futo.platformplayer.views.casting.CastView.Companion
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
@ -238,6 +240,7 @@ abstract class StateCasting {
)
}
@Throws
suspend fun castIfAvailable(contentResolver: ContentResolver, video: IPlatformVideoDetails, videoSource: IVideoSource?, audioSource: IAudioSource?, subtitleSource: ISubtitleSource?, ms: Long = -1, speed: Double?, onLoadingEstimate: ((Int) -> Unit)? = null, onLoading: ((Boolean) -> Unit)? = null): Boolean {
return withContext(Dispatchers.IO) {
val ad = activeDevice ?: return@withContext false;
@ -348,7 +351,9 @@ abstract class StateCasting {
val ad = activeDevice ?: return false;
try {
ad.resumePlayback();
} catch (_: Throwable) {
} catch (e: Throwable) {
Logger.e(TAG, "Failed to resume playback: $e")
return false
}
return true;
}
@ -357,7 +362,9 @@ abstract class StateCasting {
val ad = activeDevice ?: return false;
try {
ad.pausePlayback();
} catch (_: Throwable) {
} catch (e: Throwable) {
Logger.e(TAG, "Failed to pause playback: $e")
return false
}
return true;
}
@ -366,7 +373,9 @@ abstract class StateCasting {
val ad = activeDevice ?: return false;
try {
ad.stopPlayback();
} catch (_: Throwable) {
} catch (e: Throwable) {
Logger.e(TAG, "Failed to stop playback: $e")
return false
}
return true;
}
@ -375,11 +384,34 @@ abstract class StateCasting {
val ad = activeDevice ?: return false;
try {
ad.seekTo(timeSeconds);
} catch (_: Throwable) {
} catch (e: Throwable) {
Logger.e(TAG, "Failed to seek: $e")
return false
}
return true;
}
fun changeVolume(volume: Double): Boolean {
val ad = activeDevice ?: return false;
try {
ad.changeVolume(volume);
} catch (e: Throwable) {
Logger.e(TAG, "Failed to change volume: $e")
return false
}
return true;
}
fun changeSpeed(speed: Double): Boolean {
val ad = activeDevice ?: return false;
try {
ad.changeSpeed(speed);
} catch (e: Throwable) {
Logger.e(TAG, "Failed to change speed: $e")
return false
}
return true;
}
private fun castLocalVideo(video: IPlatformVideoDetails, videoSource: LocalVideoSource, resumePosition: Double, speed: Double?) : List<String> {
val ad = activeDevice ?: return listOf();

View file

@ -74,24 +74,18 @@ class ConnectedCastingDialog(context: Context?) : AlertDialog(context) {
_buttonPlay = findViewById(R.id.button_play);
_buttonPlay.setOnClickListener {
try {
StateCasting.instance.activeDevice?.resumePlayback()
} catch (_: Throwable) {}
StateCasting.instance.resumeVideo()
}
_buttonPause = findViewById(R.id.button_pause);
_buttonPause.setOnClickListener {
try {
StateCasting.instance.activeDevice?.pausePlayback()
} catch (_: Throwable) {}
StateCasting.instance.pauseVideo()
}
_buttonStop = findViewById(R.id.button_stop);
_buttonStop.setOnClickListener {
(ownerActivity as MainActivity?)?.getFragment<VideoDetailFragment>()?.closeVideoDetails()
try {
StateCasting.instance.activeDevice?.stopPlayback()
} catch (_: Throwable) {}
StateCasting.instance.stopVideo()
}
_buttonNext = findViewById(R.id.button_next);
@ -103,7 +97,9 @@ class ConnectedCastingDialog(context: Context?) : AlertDialog(context) {
_buttonDisconnect.setOnClickListener {
try {
StateCasting.instance.activeDevice?.disconnect()
} catch (_: Throwable) {}
} catch (e: Throwable) {
Logger.e(TAG, "Active device failed to disconnect: $e")
}
dismiss();
};
@ -112,9 +108,7 @@ class ConnectedCastingDialog(context: Context?) : AlertDialog(context) {
return@OnChangeListener
}
try {
StateCasting.instance.activeDevice?.seekTo(value.toDouble())
} catch (_: Throwable) {}
StateCasting.instance.videoSeekTo(value.toDouble())
});
//TODO: Check if volume slider is properly hidden in all cases
@ -123,9 +117,7 @@ class ConnectedCastingDialog(context: Context?) : AlertDialog(context) {
return@OnChangeListener
}
try {
StateCasting.instance.activeDevice?.changeVolume(value.toDouble())
} catch (_: Throwable) {}
StateCasting.instance.changeVolume(value.toDouble())
});
setLoading(false);

View file

@ -577,10 +577,7 @@ class VideoDetailView : ConstraintLayout {
if(chapter?.type == ChapterType.SKIPPABLE) {
_layoutSkip.visibility = VISIBLE;
} else if(chapter?.type == ChapterType.SKIP || chapter?.type == ChapterType.SKIPONCE) {
val ad = StateCasting.instance.activeDevice
if (ad != null) {
ad.seekTo(chapter.timeEnd)
} else {
if (!StateCasting.instance.videoSeekTo(chapter.timeEnd)) {
_player.seekTo((chapter.timeEnd * 1000).toLong());
}
@ -2383,9 +2380,7 @@ class VideoDetailView : ConstraintLayout {
val newPlaybackSpeed = playbackSpeedString.toDouble();
if (_isCasting && StateCasting.instance.activeDevice?.canSetSpeed() ?: false) {
qualityPlaybackSpeedTitle?.setTitle(context.getString(R.string.playback_rate) + " (${String.format(Locale.US, "%.2f", newPlaybackSpeed)})");
try {
StateCasting.instance.activeDevice?.changeSpeed(newPlaybackSpeed)
} catch (_: Throwable) {}
StateCasting.instance.changeSpeed(newPlaybackSpeed)
setSelected(playbackSpeedString);
} else {
qualityPlaybackSpeedTitle?.setTitle(context.getString(R.string.playback_rate) + " (${String.format(Locale.US, "%.2f", newPlaybackSpeed)})");

View file

@ -16,6 +16,7 @@ import com.futo.platformplayer.casting.CastProtocolType
import com.futo.platformplayer.casting.CastingDevice
import com.futo.platformplayer.casting.StateCasting
import com.futo.platformplayer.constructs.Event1
import com.futo.platformplayer.logging.Logger
class DeviceViewHolder : ViewHolder {
private val _layoutDevice: FrameLayout;
@ -63,7 +64,9 @@ class DeviceViewHolder : ViewHolder {
UIDialogs.toast(it, "Device not ready, may be offline")
}
}
} catch (_: Throwable) { }
} catch (e: Throwable) {
Logger.e(TAG, "Failed to connect: $e")
}
}
}
@ -142,4 +145,8 @@ class DeviceViewHolder : ViewHolder {
device = d;
}
companion object {
private val TAG = "DeviceViewHolder"
}
}

View file

@ -27,6 +27,7 @@ import com.futo.platformplayer.constructs.Event0
import com.futo.platformplayer.constructs.Event1
import com.futo.platformplayer.constructs.Event2
import com.futo.platformplayer.formatDuration
import com.futo.platformplayer.logging.Logger
import com.futo.platformplayer.states.StatePlayer
import com.futo.platformplayer.views.TargetTapLoaderView
import com.futo.platformplayer.views.behavior.GestureControlView
@ -96,17 +97,13 @@ class CastView : ConstraintLayout {
val d = StateCasting.instance.activeDevice ?: return@subscribe
_speedHoldWasPlaying = d.isPlaying
_speedHoldPrevRate = d.speed
if (d.canSetSpeed()) {
try {
d.changeSpeed(Settings.instance.playback.getHoldPlaybackSpeed())
} catch (e: Throwable) {
// Ignored
}
}
try {
if (d.canSetSpeed()) {
d.changeSpeed(Settings.instance.playback.getHoldPlaybackSpeed())
}
d.resumePlayback()
} catch (e: Throwable) {
// Ignored
Logger.e(TAG, "$e")
}
}
_gestureControlView.onSpeedHoldEnd.subscribe {
@ -117,16 +114,14 @@ class CastView : ConstraintLayout {
}
d.changeSpeed(_speedHoldPrevRate)
} catch (e: Throwable) {
// Ignored
Logger.e(TAG, "$e")
}
}
_gestureControlView.onSeek.subscribe {
try {
val d = StateCasting.instance.activeDevice ?: return@subscribe
val expectedCurrentTime = d.expectedCurrentTime
d.seekTo(expectedCurrentTime + it / 1000)
} catch (_: Throwable) { }
val d = StateCasting.instance.activeDevice ?: return@subscribe
val expectedCurrentTime = d.expectedCurrentTime
StateCasting.instance.videoSeekTo(expectedCurrentTime + it / 1000)
};
_buttonLoop.setOnClickListener {
@ -137,35 +132,25 @@ class CastView : ConstraintLayout {
_timeBar.addListener(object : TimeBar.OnScrubListener {
override fun onScrubStart(timeBar: TimeBar, position: Long) {
try {
StateCasting.instance.activeDevice?.seekTo(position.toDouble())
} catch (_: Throwable) { }
StateCasting.instance.videoSeekTo(position.toDouble())
}
override fun onScrubMove(timeBar: TimeBar, position: Long) {
try {
StateCasting.instance.activeDevice?.seekTo(position.toDouble())
} catch (_: Throwable) { }
StateCasting.instance.videoSeekTo(position.toDouble())
}
override fun onScrubStop(timeBar: TimeBar, position: Long, canceled: Boolean) {
try {
StateCasting.instance.activeDevice?.seekTo(position.toDouble())
} catch (_: Throwable) { }
StateCasting.instance.videoSeekTo(position.toDouble())
}
});
_buttonMinimize.setOnClickListener { onMinimizeClick.emit(); };
_buttonSettings.setOnClickListener { onSettingsClick.emit(); };
_buttonPlay.setOnClickListener {
try {
StateCasting.instance.activeDevice?.resumePlayback()
} catch (_: Throwable) { }
StateCasting.instance.resumeVideo()
}
_buttonPause.setOnClickListener {
try {
StateCasting.instance.activeDevice?.pausePlayback()
} catch (_: Throwable) { }
StateCasting.instance.pauseVideo()
}
if (!isInEditMode) {
@ -350,4 +335,8 @@ class CastView : ConstraintLayout {
_loaderGame.visibility = View.VISIBLE
_loaderGame.startLoader(expectedDurationMs.toLong())
}
companion object {
private val TAG = "CastView";
}
}