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.onMain
import com.github.damontecres.wholphin.ui.toServerString import com.github.damontecres.wholphin.ui.toServerString
import com.github.damontecres.wholphin.util.BlockingList import com.github.damontecres.wholphin.util.BlockingList
import com.github.damontecres.wholphin.util.LoadingState
import com.github.damontecres.wholphin.util.profile.Codec import com.github.damontecres.wholphin.util.profile.Codec
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -71,18 +72,19 @@ class MusicService
/** /**
* Fetches instant mix items, replaces the queue, and begins playback * Fetches instant mix items, replaces the queue, and begins playback
*/ */
suspend fun startInstantMix(itemId: UUID) { suspend fun startInstantMix(itemId: UUID) =
val items = loading {
api.instantMixApi val items =
.getInstantMixFromItem( api.instantMixApi
userId = serverRepository.currentUser.value?.id, .getInstantMixFromItem(
itemId = itemId, userId = serverRepository.currentUser.value?.id,
limit = 200, itemId = itemId,
fields = DefaultItemFields, limit = 200,
).content.items fields = DefaultItemFields,
.map { BaseItem(it, false) } ).content.items
setQueue(items, false) .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 * 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( suspend fun addAllToQueue(
list: BlockingList<BaseItem?>, list: BlockingList<BaseItem?>,
startIndex: Int, startIndex: Int,
) { ) = loading {
var remaining = startIndex var remaining = startIndex
list.indices list.indices
.chunked(25) .chunked(25)
@ -215,6 +217,13 @@ class MusicService
onMain { player.addMediaItem(state.value.currentIndex + 1, mediaItem) } onMain { player.addMediaItem(state.value.currentIndex + 1, mediaItem) }
updateQueueSize() 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 @Stable
@ -224,6 +233,7 @@ data class MusicServiceState(
val currentItemId: UUID?, val currentItemId: UUID?,
val isPlaying: Boolean, val isPlaying: Boolean,
val currentItemTitle: String?, val currentItemTitle: String?,
val loadingState: LoadingState = LoadingState.Pending,
) { ) {
companion object { companion object {
val EMPTY = MusicServiceState(0, 0, null, false, null) 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.ui.nav.Destination
import com.github.damontecres.wholphin.util.ApiRequestPager import com.github.damontecres.wholphin.util.ApiRequestPager
import com.github.damontecres.wholphin.util.BlockingList import com.github.damontecres.wholphin.util.BlockingList
import kotlinx.coroutines.delay
import org.jellyfin.sdk.api.client.ApiClient import org.jellyfin.sdk.api.client.ApiClient
import org.jellyfin.sdk.model.api.BaseItemKind import org.jellyfin.sdk.model.api.BaseItemKind
import timber.log.Timber import timber.log.Timber
@ -93,6 +94,10 @@ abstract class MusicViewModel(
viewModelScope.launchIO { viewModelScope.launchIO {
Timber.v("Starting instant mix for %s", itemId) Timber.v("Starting instant mix for %s", itemId)
musicService.startInstantMix(itemId) musicService.startInstantMix(itemId)
}
viewModelScope.launchDefault {
// TODO better way to wait for query above to start
delay(250)
navigationManager.navigateTo(Destination.NowPlaying) navigationManager.navigateTo(Destination.NowPlaying)
} }
} }

View file

@ -86,7 +86,7 @@ fun NowPlayingOverlay(
if (queueHasFocus) { if (queueHasFocus) {
1f 1f
} else { } else {
.5f .33f
}, },
animationSpec = tween(durationMillis = 500), animationSpec = tween(durationMillis = 500),
) )
@ -111,24 +111,6 @@ fun NowPlayingOverlay(
.padding(16.dp) .padding(16.dp)
.fillMaxHeight(height), .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( SeekBar(
player = player, player = player,
controllerViewState = controllerViewState, controllerViewState = controllerViewState,

View file

@ -11,6 +11,7 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.focusable import androidx.compose.foundation.focusable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize 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.compose.ui.unit.dp
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.media3.common.util.UnstableApi import androidx.media3.common.util.UnstableApi
import androidx.tv.material3.MaterialTheme
import androidx.tv.material3.Text
import coil3.compose.AsyncImage import coil3.compose.AsyncImage
import com.github.damontecres.wholphin.preferences.AppPreferences import com.github.damontecres.wholphin.preferences.AppPreferences
import com.github.damontecres.wholphin.preferences.UserPreferences import com.github.damontecres.wholphin.preferences.UserPreferences
import com.github.damontecres.wholphin.services.rememberQueue import com.github.damontecres.wholphin.services.rememberQueue
import com.github.damontecres.wholphin.ui.AppColors 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.playback.PlaybackKeyHandler
import com.github.damontecres.wholphin.ui.tryRequestFocus import com.github.damontecres.wholphin.ui.tryRequestFocus
import com.github.damontecres.wholphin.util.LoadingState
import kotlin.time.Duration.Companion.seconds import kotlin.time.Duration.Companion.seconds
@OptIn(UnstableApi::class) @OptIn(UnstableApi::class)
@ -118,15 +123,39 @@ fun NowPlayingPage(
.width(320.dp), .width(320.dp),
) )
} }
AsyncImage( Column(
contentDescription = null, horizontalAlignment = Alignment.CenterHorizontally,
model = current?.imageUrl, verticalArrangement = Arrangement.spacedBy(16.dp),
modifier = modifier =
Modifier Modifier
.padding(80.dp) .padding(40.dp)
.fillMaxSize() .fillMaxSize()
.weight(1f), .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), .align(Alignment.BottomCenter),
) )
} }
if (state.musicServiceState.loadingState is LoadingState.Loading) {
LoadingPage(focusEnabled = false)
}
} }
} }