diff --git a/app/src/main/java/dev/khaled/leanstream/channels/item/ChannelGridItem.kt b/app/src/main/java/dev/khaled/leanstream/channels/item/ChannelGridItem.kt index b84896b..4bcad8b 100644 --- a/app/src/main/java/dev/khaled/leanstream/channels/item/ChannelGridItem.kt +++ b/app/src/main/java/dev/khaled/leanstream/channels/item/ChannelGridItem.kt @@ -32,7 +32,6 @@ import androidx.compose.ui.unit.dp import androidx.tv.material3.Border import androidx.tv.material3.Card import androidx.tv.material3.CardDefaults -import androidx.tv.material3.ExperimentalTvMaterial3Api import coil.compose.AsyncImage import dev.khaled.leanstream.channels.Channel import dev.khaled.leanstream.conditional diff --git a/app/src/main/java/dev/khaled/leanstream/player/PlayerScreen.kt b/app/src/main/java/dev/khaled/leanstream/player/PlayerScreen.kt index 9a57b10..47f70c3 100644 --- a/app/src/main/java/dev/khaled/leanstream/player/PlayerScreen.kt +++ b/app/src/main/java/dev/khaled/leanstream/player/PlayerScreen.kt @@ -1,18 +1,14 @@ package dev.khaled.leanstream.player import android.content.Context -import android.net.Uri import androidx.activity.compose.BackHandler import androidx.annotation.OptIn import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.viewinterop.AndroidView -import androidx.media3.common.MediaItem -import androidx.media3.common.MediaMetadata import androidx.media3.common.util.UnstableApi import androidx.media3.exoplayer.DefaultRenderersFactory import androidx.media3.exoplayer.ExoPlayer @@ -25,20 +21,6 @@ import io.github.anilbeesetti.nextlib.media3ext.ffdecoder.NextRenderersFactory @Composable fun PlayerScreen(channel: Channel, playlist: List, navigateBack: () -> Unit) { val exoPlayer = rememberExoPlayer(LocalContext.current) - LaunchedEffect(Unit) { - // FIXME OOM When playlist large af - playlist.forEach { - exoPlayer.addMediaItem( - MediaItem.Builder().setUri(it.url).setMediaMetadata( - MediaMetadata.Builder().setDisplayTitle(it.title) - .setArtworkUri(it.icon?.let { i -> Uri.parse(i) }).build() - ).build() - ) - } - - exoPlayer.seekTo(playlist.indexOf(channel), 0) - exoPlayer.prepare() - } BackHandler(onBack = navigateBack) @@ -51,7 +33,11 @@ fun PlayerScreen(channel: Channel, playlist: List, navigateBack: () -> PlayerController( - modifier = Modifier.fillMaxSize(), player = exoPlayer, backHandler = navigateBack + modifier = Modifier.fillMaxSize(), + player = exoPlayer, + playlist = playlist, + initialChannel = channel, + backHandler = navigateBack ) } @@ -63,8 +49,7 @@ private fun rememberExoPlayer(context: Context) = remember { DefaultRenderersFactory.EXTENSION_RENDERER_MODE_ON ) - ExoPlayer.Builder(context).setRenderersFactory(renderersFactory) - .build().apply { + ExoPlayer.Builder(context).setRenderersFactory(renderersFactory).build().apply { playWhenReady = true } } diff --git a/app/src/main/java/dev/khaled/leanstream/player/controller/PlayerController.kt b/app/src/main/java/dev/khaled/leanstream/player/controller/PlayerController.kt index 19bb1f6..e3c83c3 100644 --- a/app/src/main/java/dev/khaled/leanstream/player/controller/PlayerController.kt +++ b/app/src/main/java/dev/khaled/leanstream/player/controller/PlayerController.kt @@ -1,6 +1,7 @@ package dev.khaled.leanstream.player.controller import android.content.pm.ActivityInfo +import android.net.Uri import android.os.Handler import android.os.Looper import androidx.compose.animation.AnimatedVisibility @@ -19,7 +20,9 @@ import androidx.compose.material3.FilledTonalIconButton import androidx.compose.material3.Icon import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue @@ -30,27 +33,32 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp import androidx.media3.common.MediaItem +import androidx.media3.common.MediaMetadata import androidx.media3.common.PlaybackException import androidx.media3.common.Player import androidx.media3.common.Player.STATE_BUFFERING import androidx.media3.exoplayer.ExoPlayer +import dev.khaled.leanstream.channels.Channel import dev.khaled.leanstream.conditional import dev.khaled.leanstream.isRunningOnTV import dev.khaled.leanstream.ui.ScreenOrientation + @Composable fun PlayerController( modifier: Modifier = Modifier, player: ExoPlayer, backHandler: () -> Unit, + playlist: List, + initialChannel: Channel, ) { - val isRunningOnTV = isRunningOnTV(LocalContext.current) var controllerVisible by remember { mutableStateOf(true) } var isButtonFocused by remember { mutableStateOf(true) } + var currentChannelIndex by remember { mutableIntStateOf(playlist.indexOf(initialChannel)) } var mediaMetadata by remember { mutableStateOf(player.mediaMetadata) } var isPlaying by remember { mutableStateOf(true) } @@ -71,6 +79,17 @@ fun PlayerController( handler.postDelayed(controllerVisibilityRunnable, 3000) } + fun loadChannel(index: Int) { + val channel = playlist[index] + player.setMediaItem( + MediaItem.Builder().setUri(channel.url).setMediaMetadata( + MediaMetadata.Builder().setDisplayTitle(channel.title) + .setArtworkUri(channel.icon?.let { i -> Uri.parse(i) }).build() + ).build() + ) + player.prepare() + } + DisposableEffect(Unit) { val listener = object : Player.Listener { override fun onIsPlayingChanged(playing: Boolean) { @@ -93,9 +112,9 @@ fun PlayerController( player.prepare() //TODO Auto Retry Toggle } - override fun onMediaItemTransition(mediaItem: MediaItem?, reason: Int) { - super.onMediaItemTransition(mediaItem, reason) - mediaItem?.let { mediaMetadata = it.mediaMetadata } + override fun onMediaMetadataChanged(metadata: MediaMetadata) { + super.onMediaMetadataChanged(metadata) + mediaMetadata = metadata } } @@ -103,6 +122,7 @@ fun PlayerController( onDispose { player.removeListener(listener) } } + LaunchedEffect(Unit) { loadChannel(currentChannelIndex) } ScreenOrientation(orientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) @@ -154,20 +174,18 @@ fun PlayerController( Spacer(modifier = Modifier.weight(1f)) - FilledTonalIconButton(enabled = player.hasPreviousMediaItem(), - onClick = { player.seekToPreviousMediaItem() }) { + FilledTonalIconButton(enabled = currentChannelIndex > 0, + onClick = { loadChannel(--currentChannelIndex) }) { Icon(Icons.Rounded.SkipPrevious, contentDescription = null) } ChannelInfo(mediaMetadata) - FilledTonalIconButton(enabled = player.hasNextMediaItem(), - onClick = { player.seekToNextMediaItem() }) { + FilledTonalIconButton(enabled = currentChannelIndex < playlist.size, + onClick = { loadChannel(++currentChannelIndex) }) { Icon(Icons.Rounded.SkipNext, contentDescription = null) } } - - } } }