diff --git a/.gitignore b/.gitignore index 36c8150..51cd769 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,5 @@ android/.gradle/ .build www/ android/app/src/main/assets/ -release/ \ No newline at end of file +release/ +android/app/src/main/res/raw/*.crt \ No newline at end of file diff --git a/android/app/src/main/java/tv/vyla/app/ui/screens/PlayerScreen.kt b/android/app/src/main/java/tv/vyla/app/ui/screens/PlayerScreen.kt index 97e5b49..52a7212 100644 --- a/android/app/src/main/java/tv/vyla/app/ui/screens/PlayerScreen.kt +++ b/android/app/src/main/java/tv/vyla/app/ui/screens/PlayerScreen.kt @@ -79,6 +79,7 @@ import kotlinx.coroutines.flow.first import kotlinx.coroutines.withTimeoutOrNull import tv.vyla.app.ui.components.VideoTintSampler import tv.vyla.app.ui.components.videoTint +import android.util.Log enum class ActivePlayerMenu { NONE, QUALITY, SUBTITLES, VOLUME, SETTINGS, MORE @@ -224,6 +225,33 @@ fun PlayerScreen( exoPlayer.play() resetHideTimer() } + DisposableEffect(exoPlayer) { + val errorListener = object : Player.Listener { // source auto-fallback + override fun onPlayerError(error: androidx.media3.common.PlaybackException) { + val currentIdx = resolvedStreams.indexOf(activeStream) + if (currentIdx != -1 && currentIdx + 1 < resolvedStreams.size) { + // android.widget.Toast.makeText(context, "Source failed. Trying next...", android.widget.Toast.LENGTH_SHORT).show() + Log.e("vyla", "Source failed. Trying next...") + + coroutineScope.launch { + delay(1500) // follow rate limits + val nextStream = resolvedStreams[currentIdx + 1] + activeStream = nextStream + playStream(nextStream.primaryUrl, nextStream.headers) + } + } else { + isBuffering = false + Log.e("vyla", "All Sources Failed") + android.widget.Toast.makeText(context, "All available sources failed. Please disable any VPN or try again later", android.widget.Toast.LENGTH_LONG).show() + } + } + } + exoPlayer.addListener(errorListener) + + onDispose { + exoPlayer.removeListener(errorListener) + } +} suspend fun focusEpisodeCard() { var tries = 0 while (tries < 15) { @@ -300,7 +328,9 @@ fun PlayerScreen( onFirstPlayable = { stream -> coroutineScope.launch { activeStream = stream - resolvedStreams.add(stream) + if (!resolvedStreams.any { it.provider == stream.provider }) { + resolvedStreams.add(stream) + } playStream(stream.primaryUrl, stream.headers) } }, @@ -938,7 +968,7 @@ fun PlayerScreen( items(resolvedStreams) { stream -> MenuItemRow( label = stream.label, - isSelected = activeStream?.provider == stream.provider, + isSelected = ((activeStream?.provider == stream.provider) && (activeStream?.primaryUrl == stream.primaryUrl)), onClick = { activeStream = stream playStream(stream.primaryUrl, stream.headers) diff --git a/android/gradlew b/android/gradlew old mode 100644 new mode 100755