Tweak player code

This commit is contained in:
vfsfitvnm
2022-10-16 20:42:16 +02:00
parent 7869f1a388
commit 6ebb5dfc65
8 changed files with 186 additions and 200 deletions

View File

@@ -45,10 +45,10 @@ import it.vfsfitvnm.vimusic.ui.components.SeekBar
import it.vfsfitvnm.vimusic.ui.components.themed.IconButton
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
import it.vfsfitvnm.vimusic.ui.styling.favoritesIcon
import it.vfsfitvnm.vimusic.utils.DisposableListener
import it.vfsfitvnm.vimusic.utils.bold
import it.vfsfitvnm.vimusic.utils.forceSeekToNext
import it.vfsfitvnm.vimusic.utils.forceSeekToPrevious
import it.vfsfitvnm.vimusic.utils.rememberRepeatMode
import it.vfsfitvnm.vimusic.utils.secondary
import it.vfsfitvnm.vimusic.utils.semiBold
import kotlinx.coroutines.Dispatchers
@@ -70,7 +70,17 @@ fun Controls(
val binder = LocalPlayerServiceBinder.current
binder?.player ?: return
val repeatMode by rememberRepeatMode(binder.player)
var repeatMode by remember {
mutableStateOf(binder.player.repeatMode)
}
binder.player.DisposableListener {
object : Player.Listener {
override fun onRepeatModeChanged(newRepeatMode: Int) {
repeatMode = newRepeatMode
}
}
}
var scrubbingPosition by remember(mediaId) {
mutableStateOf<Long?>(null)

View File

@@ -29,6 +29,8 @@ import androidx.compose.foundation.text.BasicText
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.neverEqualPolicy
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
@@ -44,6 +46,7 @@ import androidx.compose.ui.unit.dp
import androidx.media3.common.MediaItem
import androidx.media3.common.Player
import coil.compose.AsyncImage
import it.vfsfitvnm.innertube.models.NavigationEndpoint
import it.vfsfitvnm.route.OnGlobalRoute
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
import it.vfsfitvnm.vimusic.R
@@ -58,16 +61,15 @@ import it.vfsfitvnm.vimusic.ui.styling.Dimensions
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
import it.vfsfitvnm.vimusic.ui.styling.collapsedPlayerProgressBar
import it.vfsfitvnm.vimusic.ui.styling.px
import it.vfsfitvnm.vimusic.utils.DisposableListener
import it.vfsfitvnm.vimusic.utils.forceSeekToNext
import it.vfsfitvnm.vimusic.utils.isLandscape
import it.vfsfitvnm.vimusic.utils.rememberMediaItem
import it.vfsfitvnm.vimusic.utils.rememberPositionAndDuration
import it.vfsfitvnm.vimusic.utils.rememberShouldBePlaying
import it.vfsfitvnm.vimusic.utils.positionAndDurationState
import it.vfsfitvnm.vimusic.utils.seamlessPlay
import it.vfsfitvnm.vimusic.utils.secondary
import it.vfsfitvnm.vimusic.utils.semiBold
import it.vfsfitvnm.vimusic.utils.shouldBePlaying
import it.vfsfitvnm.vimusic.utils.thumbnail
import it.vfsfitvnm.innertube.models.NavigationEndpoint
import kotlin.math.absoluteValue
@ExperimentalFoundationApi
@@ -84,12 +86,33 @@ fun Player(
binder?.player ?: return
val nullableMediaItem by rememberMediaItem(binder.player)
var nullableMediaItem by remember {
mutableStateOf(binder.player.currentMediaItem, neverEqualPolicy())
}
var shouldBePlaying by remember {
mutableStateOf(binder.player.shouldBePlaying)
}
binder.player.DisposableListener {
object : Player.Listener {
override fun onMediaItemTransition(mediaItem: MediaItem?, reason: Int) {
nullableMediaItem = mediaItem
}
override fun onPlayWhenReadyChanged(playWhenReady: Boolean, reason: Int) {
shouldBePlaying = binder.player.shouldBePlaying
}
override fun onPlaybackStateChanged(playbackState: Int) {
shouldBePlaying = binder.player.shouldBePlaying
}
}
}
val mediaItem = nullableMediaItem ?: return
val shouldBePlaying by rememberShouldBePlaying(binder.player)
val positionAndDuration by rememberPositionAndDuration(binder.player)
val positionAndDuration by binder.player.positionAndDurationState()
val windowInsets = WindowInsets.systemBars

View File

@@ -31,6 +31,9 @@ import androidx.compose.foundation.text.BasicText
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
@@ -40,6 +43,9 @@ import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.media3.common.MediaItem
import androidx.media3.common.Player
import androidx.media3.common.Timeline
import com.valentinilk.shimmer.shimmer
import it.vfsfitvnm.reordering.ReorderingLazyColumn
import it.vfsfitvnm.reordering.animateItemPlacement
@@ -61,12 +67,12 @@ import it.vfsfitvnm.vimusic.ui.styling.Dimensions
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
import it.vfsfitvnm.vimusic.ui.styling.onOverlay
import it.vfsfitvnm.vimusic.ui.styling.px
import it.vfsfitvnm.vimusic.utils.DisposableListener
import it.vfsfitvnm.vimusic.utils.medium
import it.vfsfitvnm.vimusic.utils.rememberMediaItemIndex
import it.vfsfitvnm.vimusic.utils.rememberShouldBePlaying
import it.vfsfitvnm.vimusic.utils.rememberWindows
import it.vfsfitvnm.vimusic.utils.shouldBePlaying
import it.vfsfitvnm.vimusic.utils.shuffleQueue
import it.vfsfitvnm.vimusic.utils.smoothScrollToTop
import it.vfsfitvnm.vimusic.utils.windows
import kotlinx.coroutines.launch
@ExperimentalFoundationApi
@@ -112,19 +118,52 @@ fun Queue(
binder?.player ?: return@BottomSheet
val player = binder.player
val menuState = LocalMenuState.current
val thumbnailSizeDp = Dimensions.thumbnails.song
val thumbnailSizePx = thumbnailSizeDp.px
val mediaItemIndex by rememberMediaItemIndex(binder.player)
val windows by rememberWindows(binder.player)
val shouldBePlaying by rememberShouldBePlaying(binder.player)
var mediaItemIndex by remember {
mutableStateOf(if (player.mediaItemCount == 0) -1 else player.currentMediaItemIndex)
}
var windows by remember {
mutableStateOf(player.currentTimeline.windows)
}
var shouldBePlaying by remember {
mutableStateOf(binder.player.shouldBePlaying)
}
player.DisposableListener {
object : Player.Listener {
override fun onMediaItemTransition(mediaItem: MediaItem?, reason: Int) {
mediaItemIndex =
if (player.mediaItemCount == 0) -1 else player.currentMediaItemIndex
}
override fun onTimelineChanged(timeline: Timeline, reason: Int) {
windows = timeline.windows
mediaItemIndex =
if (player.mediaItemCount == 0) -1 else player.currentMediaItemIndex
}
override fun onPlayWhenReadyChanged(playWhenReady: Boolean, reason: Int) {
shouldBePlaying = binder.player.shouldBePlaying
}
override fun onPlaybackStateChanged(playbackState: Int) {
shouldBePlaying = binder.player.shouldBePlaying
}
}
}
val reorderingState = rememberReorderingState(
lazyListState = rememberLazyListState(initialFirstVisibleItemIndex = mediaItemIndex),
key = windows,
onDragEnd = binder.player::moveMediaItem,
onDragEnd = player::moveMediaItem,
extraItemCount = 0
)
@@ -219,13 +258,13 @@ fun Queue(
onClick = {
if (isPlayingThisMediaItem) {
if (shouldBePlaying) {
binder.player.pause()
player.pause()
} else {
binder.player.play()
player.play()
}
} else {
binder.player.playWhenReady = true
binder.player.seekToDefaultPosition(window.firstPeriodIndex)
player.playWhenReady = true
player.seekToDefaultPosition(window.firstPeriodIndex)
}
}
)
@@ -266,7 +305,7 @@ fun Queue(
reorderingState.coroutineScope.launch {
reorderingState.lazyListState.smoothScrollToTop()
}.invokeOnCompletion {
binder.player.shuffleQueue()
player.shuffleQueue()
}
}
)

View File

@@ -26,8 +26,12 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.media3.common.Player
import androidx.media3.datasource.cache.Cache
import androidx.media3.datasource.cache.CacheSpan
import it.vfsfitvnm.innertube.Innertube
import it.vfsfitvnm.innertube.models.bodies.PlayerBody
import it.vfsfitvnm.innertube.requests.player
import it.vfsfitvnm.vimusic.Database
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
import it.vfsfitvnm.vimusic.models.Format
@@ -35,12 +39,9 @@ import it.vfsfitvnm.vimusic.query
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
import it.vfsfitvnm.vimusic.ui.styling.onOverlay
import it.vfsfitvnm.vimusic.ui.styling.overlay
import it.vfsfitvnm.vimusic.utils.DisposableListener
import it.vfsfitvnm.vimusic.utils.color
import it.vfsfitvnm.vimusic.utils.medium
import it.vfsfitvnm.vimusic.utils.rememberVolume
import it.vfsfitvnm.innertube.Innertube
import it.vfsfitvnm.innertube.models.bodies.PlayerBody
import it.vfsfitvnm.innertube.requests.player
import kotlin.math.roundToInt
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.distinctUntilChanged
@@ -70,7 +71,17 @@ fun StatsForNerds(
Database.format(mediaId).distinctUntilChanged()
}.collectAsState(initial = null, context = Dispatchers.IO)
val volume by rememberVolume(binder.player)
var volume by remember {
mutableStateOf(binder.player.volume)
}
binder.player.DisposableListener {
object : Player.Listener {
override fun onVolumeChanged(newVolume: Float) {
volume = newVolume
}
}
}
DisposableEffect(mediaId) {
val listener = object : Cache.Listener {
@@ -193,7 +204,8 @@ fun StatsForNerds(
onClick = {
query {
runBlocking(Dispatchers.IO) {
Innertube.player(PlayerBody(videoId = mediaId))
Innertube
.player(PlayerBody(videoId = mediaId))
?.map { response ->
response.streamingData?.adaptiveFormats
?.findLast { format ->
@@ -205,7 +217,9 @@ fun StatsForNerds(
itag = format.itag,
mimeType = format.mimeType,
bitrate = format.bitrate,
loudnessDb = response.playerConfig?.audioConfig?.loudnessDb?.toFloat()?.plus(7),
loudnessDb = response.playerConfig?.audioConfig?.loudnessDb
?.toFloat()
?.plus(7),
contentLength = format.contentLength,
lastModified = format.lastModified
)

View File

@@ -17,12 +17,18 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.unit.dp
import androidx.media3.common.MediaItem
import androidx.media3.common.PlaybackException
import androidx.media3.common.Player
import coil.compose.AsyncImage
import it.vfsfitvnm.vimusic.Database
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
@@ -34,9 +40,8 @@ import it.vfsfitvnm.vimusic.service.VideoIdMismatchException
import it.vfsfitvnm.vimusic.ui.styling.Dimensions
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
import it.vfsfitvnm.vimusic.ui.styling.px
import it.vfsfitvnm.vimusic.utils.rememberError
import it.vfsfitvnm.vimusic.utils.rememberMediaItem
import it.vfsfitvnm.vimusic.utils.rememberMediaItemIndex
import it.vfsfitvnm.vimusic.utils.currentWindow
import it.vfsfitvnm.vimusic.utils.DisposableListener
import it.vfsfitvnm.vimusic.utils.thumbnail
import java.net.UnknownHostException
import java.nio.channels.UnresolvedAddressException
@@ -57,17 +62,38 @@ fun Thumbnail(
it to (it - 64.dp).px
}
val mediaItemIndex by rememberMediaItemIndex(player)
val mediaItem by rememberMediaItem(player)
var nullableWindow by remember {
mutableStateOf(player.currentWindow)
}
val error by rememberError(player)
var error by remember {
mutableStateOf<PlaybackException?>(player.playerError)
}
player.DisposableListener {
object : Player.Listener {
override fun onMediaItemTransition(mediaItem: MediaItem?, reason: Int) {
nullableWindow = player.currentWindow
}
override fun onPlaybackStateChanged(playbackState: Int) {
error = player.playerError
}
override fun onPlayerError(playbackException: PlaybackException) {
error = playbackException
}
}
}
val window = nullableWindow ?: return
AnimatedContent(
targetState = mediaItemIndex to mediaItem,
targetState = window,
transitionSpec = {
val duration = 500
val slideDirection =
if (targetState.first > initialState.first) AnimatedContentScope.SlideDirection.Left else AnimatedContentScope.SlideDirection.Right
if (targetState.firstPeriodIndex > initialState.firstPeriodIndex) AnimatedContentScope.SlideDirection.Left else AnimatedContentScope.SlideDirection.Right
ContentTransform(
targetContentEnter = slideIntoContainer(
@@ -92,9 +118,7 @@ fun Thumbnail(
)
},
contentAlignment = Alignment.Center
) { (_, currentMediaItem) ->
val currentMediaItem = currentMediaItem ?: return@AnimatedContent
) {currentWindow ->
Box(
modifier = modifier
.aspectRatio(1f)
@@ -102,7 +126,7 @@ fun Thumbnail(
.size(thumbnailSizeDp)
) {
AsyncImage(
model = currentMediaItem.mediaMetadata.artworkUri.thumbnail(thumbnailSizePx),
model = currentWindow.mediaItem.mediaMetadata.artworkUri.thumbnail(thumbnailSizePx),
contentDescription = null,
contentScale = ContentScale.Crop,
modifier = Modifier
@@ -116,23 +140,23 @@ fun Thumbnail(
)
Lyrics(
mediaId = currentMediaItem.mediaId,
mediaId = currentWindow.mediaItem.mediaId,
isDisplayed = isShowingLyrics && error == null,
onDismiss = { onShowLyrics(false) },
onLyricsUpdate = { areSynchronized, mediaId, lyrics ->
query {
if (areSynchronized) {
if (Database.updateSynchronizedLyrics(mediaId, lyrics) == 0) {
if (mediaId == currentMediaItem.mediaId) {
Database.insert(currentMediaItem) { song ->
if (mediaId == currentWindow.mediaItem.mediaId) {
Database.insert(currentWindow.mediaItem) { song ->
song.copy(synchronizedLyrics = lyrics)
}
}
}
} else {
if (Database.updateLyrics(mediaId, lyrics) == 0) {
if (mediaId == currentMediaItem.mediaId) {
Database.insert(currentMediaItem) { song ->
if (mediaId == currentWindow.mediaItem.mediaId) {
Database.insert(currentWindow.mediaItem) { song ->
song.copy(lyrics = lyrics)
}
}
@@ -141,12 +165,12 @@ fun Thumbnail(
}
},
size = thumbnailSizeDp,
mediaMetadataProvider = currentMediaItem::mediaMetadata,
mediaMetadataProvider = currentWindow.mediaItem::mediaMetadata,
durationProvider = player::getDuration,
)
StatsForNerds(
mediaId = currentMediaItem.mediaId,
mediaId = currentWindow.mediaItem.mediaId,
isDisplayed = isShowingStatsForNerds && error == null,
onDismiss = { onShowStatsForNerds(false) }
)