Move now playing song title to page

This commit is contained in:
Damontecres 2026-03-02 16:36:49 -05:00
parent 4de4144648
commit 7044b4aa32
No known key found for this signature in database
4 changed files with 66 additions and 37 deletions

View file

@ -21,6 +21,7 @@ import com.github.damontecres.wholphin.ui.main.settings.MoveDirection
import com.github.damontecres.wholphin.ui.onMain
import com.github.damontecres.wholphin.ui.toServerString
import com.github.damontecres.wholphin.util.BlockingList
import com.github.damontecres.wholphin.util.LoadingState
import com.github.damontecres.wholphin.util.profile.Codec
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.Dispatchers
@ -71,18 +72,19 @@ class MusicService
/**
* Fetches instant mix items, replaces the queue, and begins playback
*/
suspend fun startInstantMix(itemId: UUID) {
val items =
api.instantMixApi
.getInstantMixFromItem(
userId = serverRepository.currentUser.value?.id,
itemId = itemId,
limit = 200,
fields = DefaultItemFields,
).content.items
.map { BaseItem(it, false) }
setQueue(items, false)
}
suspend fun startInstantMix(itemId: UUID) =
loading {
val items =
api.instantMixApi
.getInstantMixFromItem(
userId = serverRepository.currentUser.value?.id,
itemId = itemId,
limit = 200,
fields = DefaultItemFields,
).content.items
.map { BaseItem(it, false) }
setQueue(items, false)
}
/**
* Replace the queue with the given list and starting playing the song as startIndex as soon as its ready
@ -144,7 +146,7 @@ class MusicService
suspend fun addAllToQueue(
list: BlockingList<BaseItem?>,
startIndex: Int,
) {
) = loading {
var remaining = startIndex
list.indices
.chunked(25)
@ -215,6 +217,13 @@ class MusicService
onMain { player.addMediaItem(state.value.currentIndex + 1, mediaItem) }
updateQueueSize()
}
private suspend fun <T> loading(block: suspend () -> T): T {
_state.update { it.copy(loadingState = LoadingState.Loading) }
val result = block.invoke()
_state.update { it.copy(loadingState = LoadingState.Success) }
return result
}
}
@Stable
@ -224,6 +233,7 @@ data class MusicServiceState(
val currentItemId: UUID?,
val isPlaying: Boolean,
val currentItemTitle: String?,
val loadingState: LoadingState = LoadingState.Pending,
) {
companion object {
val EMPTY = MusicServiceState(0, 0, null, false, null)

View file

@ -10,6 +10,7 @@ import com.github.damontecres.wholphin.ui.launchIO
import com.github.damontecres.wholphin.ui.nav.Destination
import com.github.damontecres.wholphin.util.ApiRequestPager
import com.github.damontecres.wholphin.util.BlockingList
import kotlinx.coroutines.delay
import org.jellyfin.sdk.api.client.ApiClient
import org.jellyfin.sdk.model.api.BaseItemKind
import timber.log.Timber
@ -93,6 +94,10 @@ abstract class MusicViewModel(
viewModelScope.launchIO {
Timber.v("Starting instant mix for %s", itemId)
musicService.startInstantMix(itemId)
}
viewModelScope.launchDefault {
// TODO better way to wait for query above to start
delay(250)
navigationManager.navigateTo(Destination.NowPlaying)
}
}

View file

@ -86,7 +86,7 @@ fun NowPlayingOverlay(
if (queueHasFocus) {
1f
} else {
.5f
.33f
},
animationSpec = tween(durationMillis = 500),
)
@ -111,24 +111,6 @@ fun NowPlayingOverlay(
.padding(16.dp)
.fillMaxHeight(height),
) {
current?.title?.let {
Text(
text = it,
style = MaterialTheme.typography.titleMedium,
)
}
current?.albumTitle?.let {
Text(
text = it,
style = MaterialTheme.typography.titleSmall,
)
}
current?.artistNames?.let {
Text(
text = it,
style = MaterialTheme.typography.titleSmall,
)
}
SeekBar(
player = player,
controllerViewState = controllerViewState,

View file

@ -11,6 +11,7 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.focusable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
@ -29,13 +30,17 @@ import androidx.compose.ui.input.key.onPreviewKeyEvent
import androidx.compose.ui.unit.dp
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.media3.common.util.UnstableApi
import androidx.tv.material3.MaterialTheme
import androidx.tv.material3.Text
import coil3.compose.AsyncImage
import com.github.damontecres.wholphin.preferences.AppPreferences
import com.github.damontecres.wholphin.preferences.UserPreferences
import com.github.damontecres.wholphin.services.rememberQueue
import com.github.damontecres.wholphin.ui.AppColors
import com.github.damontecres.wholphin.ui.components.LoadingPage
import com.github.damontecres.wholphin.ui.playback.PlaybackKeyHandler
import com.github.damontecres.wholphin.ui.tryRequestFocus
import com.github.damontecres.wholphin.util.LoadingState
import kotlin.time.Duration.Companion.seconds
@OptIn(UnstableApi::class)
@ -118,15 +123,39 @@ fun NowPlayingPage(
.width(320.dp),
)
}
AsyncImage(
contentDescription = null,
model = current?.imageUrl,
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(16.dp),
modifier =
Modifier
.padding(80.dp)
.padding(40.dp)
.fillMaxSize()
.weight(1f),
)
) {
AsyncImage(
contentDescription = null,
model = current?.imageUrl,
modifier = Modifier.fillMaxSize(.7f),
)
current?.title?.let {
Text(
text = it,
style = MaterialTheme.typography.titleLarge,
)
}
current?.albumTitle?.let {
Text(
text = it,
style = MaterialTheme.typography.titleMedium,
)
}
current?.artistNames?.let {
Text(
text = it,
style = MaterialTheme.typography.titleMedium,
)
}
}
}
}
@ -154,5 +183,8 @@ fun NowPlayingPage(
.align(Alignment.BottomCenter),
)
}
if (state.musicServiceState.loadingState is LoadingState.Loading) {
LoadingPage(focusEnabled = false)
}
}
}