mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-11-06 18:54:49 +01:00
Improvements to Now Playing page, including artist backdrops (#1782)
## Description This PR has updates the Now Playing page. Now only the song's backdrops and the song's artists' backdrops will show. First, if the song has any backdrops (probably pretty uncommon), they will be shuffled and shown, then if the song's artists have backdrop, those are shuffled and shown. The backdrop changes every 2 minutes unless there's <15s left in the song to prevent rapid changes. Note: album backdrops and album artists not directly linked to the specific song are not included. If a song has a lyrics and the user is scrolling through them, if the user doesn't scroll for 5 seconds, the focus leaves the lyrics and they scroll to the current line if available. Dev note: Both backdrop & lyric updates are refactored to use `Flow`s to trigger lookups ### Related issues Fixes #1170 ### Testing Emulator, I need to test it longer term ## Screenshots N/A ## AI or LLM usage None
This commit is contained in:
parent
5219b22fc7
commit
de30c2ccac
4 changed files with 137 additions and 139 deletions
|
|
@ -121,7 +121,10 @@ fun LyricsContent(
|
||||||
),
|
),
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
.focusRequester(focusRequesters[index]),
|
.focusRequester(focusRequesters[index])
|
||||||
|
.onFocusChanged {
|
||||||
|
if (it.isFocused) onFocusLyrics.invoke(true)
|
||||||
|
},
|
||||||
) {
|
) {
|
||||||
val text =
|
val text =
|
||||||
remember(lyric.text) { lyric.text.ifBlank { " " } }
|
remember(lyric.text) { lyric.text.ifBlank { " " } }
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,9 @@ import com.github.damontecres.wholphin.ui.playback.overlay.BottomDialog
|
||||||
import com.github.damontecres.wholphin.ui.playback.overlay.BottomDialogItem
|
import com.github.damontecres.wholphin.ui.playback.overlay.BottomDialogItem
|
||||||
import com.github.damontecres.wholphin.ui.tryRequestFocus
|
import com.github.damontecres.wholphin.ui.tryRequestFocus
|
||||||
import com.github.damontecres.wholphin.util.LoadingState
|
import com.github.damontecres.wholphin.util.LoadingState
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
import org.jellyfin.sdk.model.extensions.ticks
|
import org.jellyfin.sdk.model.extensions.ticks
|
||||||
|
import java.util.Date
|
||||||
import kotlin.time.Duration.Companion.seconds
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
@OptIn(UnstableApi::class)
|
@OptIn(UnstableApi::class)
|
||||||
|
|
@ -140,19 +142,22 @@ fun NowPlayingPage(
|
||||||
var showViewOptionsDialog by remember { mutableStateOf(false) }
|
var showViewOptionsDialog by remember { mutableStateOf(false) }
|
||||||
var showContextMenu by remember { mutableStateOf<ContextMenu.ForQueue?>(null) }
|
var showContextMenu by remember { mutableStateOf<ContextMenu.ForQueue?>(null) }
|
||||||
|
|
||||||
var lyricsHaveFocus by remember { mutableStateOf(false) }
|
var lyricsFocused by remember { mutableStateOf<Date?>(null) }
|
||||||
|
|
||||||
val focusRequester = remember { FocusRequester() }
|
val focusRequester = remember { FocusRequester() }
|
||||||
LaunchedEffect(Unit) { focusRequester.tryRequestFocus() }
|
LaunchedEffect(Unit) { focusRequester.tryRequestFocus() }
|
||||||
val lyricsFocusRequester = remember { FocusRequester() }
|
val lyricsFocusRequester = remember { FocusRequester() }
|
||||||
val hasLyrics = musicPrefs.showLyrics && state.hasLyrics
|
val hasLyrics = musicPrefs.showLyrics && state.hasLyrics
|
||||||
|
|
||||||
LaunchedEffect(lyricsHaveFocus) {
|
LaunchedEffect(lyricsFocused) {
|
||||||
if (lyricsHaveFocus) {
|
if (lyricsFocused != null) {
|
||||||
controllerViewState.hideControls()
|
controllerViewState.hideControls()
|
||||||
}
|
}
|
||||||
|
delay(5.seconds)
|
||||||
|
lyricsFocused = null
|
||||||
|
focusRequester.tryRequestFocus()
|
||||||
}
|
}
|
||||||
BackHandler(lyricsHaveFocus) {
|
BackHandler(lyricsFocused != null) {
|
||||||
focusRequester.tryRequestFocus()
|
focusRequester.tryRequestFocus()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -290,8 +295,10 @@ fun NowPlayingPage(
|
||||||
LyricsContent(
|
LyricsContent(
|
||||||
lyrics = state.lyrics,
|
lyrics = state.lyrics,
|
||||||
currentLyricPosition = state.currentLyricIndex,
|
currentLyricPosition = state.currentLyricIndex,
|
||||||
lyricsHaveFocus = lyricsHaveFocus,
|
lyricsHaveFocus = lyricsFocused != null,
|
||||||
onFocusLyrics = { lyricsHaveFocus = it },
|
onFocusLyrics = {
|
||||||
|
lyricsFocused = Date()
|
||||||
|
},
|
||||||
onClick = {
|
onClick = {
|
||||||
it.start
|
it.start
|
||||||
?.ticks
|
?.ticks
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package com.github.damontecres.wholphin.ui.detail.music
|
package com.github.damontecres.wholphin.ui.detail.music
|
||||||
|
|
||||||
import androidx.compose.runtime.Stable
|
import androidx.compose.runtime.Stable
|
||||||
|
import com.github.damontecres.wholphin.data.model.AudioItem
|
||||||
import com.github.damontecres.wholphin.services.BackdropResult
|
import com.github.damontecres.wholphin.services.BackdropResult
|
||||||
import com.github.damontecres.wholphin.services.MusicServiceState
|
import com.github.damontecres.wholphin.services.MusicServiceState
|
||||||
import org.jellyfin.sdk.model.api.LyricDto
|
import org.jellyfin.sdk.model.api.LyricDto
|
||||||
|
|
@ -8,6 +9,7 @@ import org.jellyfin.sdk.model.api.LyricDto
|
||||||
@Stable
|
@Stable
|
||||||
data class NowPlayingState(
|
data class NowPlayingState(
|
||||||
val musicServiceState: MusicServiceState,
|
val musicServiceState: MusicServiceState,
|
||||||
|
val currentMediaItem: AudioItem? = null,
|
||||||
val lyrics: LyricDto? = null,
|
val lyrics: LyricDto? = null,
|
||||||
val currentLyricIndex: Int? = null,
|
val currentLyricIndex: Int? = null,
|
||||||
val visualizerPermissions: Boolean = false,
|
val visualizerPermissions: Boolean = false,
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@ import androidx.media3.common.MediaItem
|
||||||
import androidx.media3.common.Player
|
import androidx.media3.common.Player
|
||||||
import androidx.media3.common.util.UnstableApi
|
import androidx.media3.common.util.UnstableApi
|
||||||
import com.github.damontecres.wholphin.data.model.AudioItem
|
import com.github.damontecres.wholphin.data.model.AudioItem
|
||||||
import com.github.damontecres.wholphin.data.model.BaseItem
|
|
||||||
import com.github.damontecres.wholphin.preferences.AppPreference
|
import com.github.damontecres.wholphin.preferences.AppPreference
|
||||||
import com.github.damontecres.wholphin.preferences.AppPreferences
|
import com.github.damontecres.wholphin.preferences.AppPreferences
|
||||||
import com.github.damontecres.wholphin.preferences.updateMusicPreferences
|
import com.github.damontecres.wholphin.preferences.updateMusicPreferences
|
||||||
|
|
@ -23,40 +22,43 @@ import com.github.damontecres.wholphin.services.MusicService
|
||||||
import com.github.damontecres.wholphin.services.NavigationManager
|
import com.github.damontecres.wholphin.services.NavigationManager
|
||||||
import com.github.damontecres.wholphin.services.NowPlayingStatus
|
import com.github.damontecres.wholphin.services.NowPlayingStatus
|
||||||
import com.github.damontecres.wholphin.services.UserPreferencesService
|
import com.github.damontecres.wholphin.services.UserPreferencesService
|
||||||
|
import com.github.damontecres.wholphin.ui.combinePair
|
||||||
import com.github.damontecres.wholphin.ui.launchDefault
|
import com.github.damontecres.wholphin.ui.launchDefault
|
||||||
import com.github.damontecres.wholphin.ui.main.settings.MoveDirection
|
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.playback.ControllerViewState
|
import com.github.damontecres.wholphin.ui.playback.ControllerViewState
|
||||||
|
import com.github.damontecres.wholphin.util.GetItemsRequestHandler
|
||||||
import com.github.damontecres.wholphin.util.WholphinDispatchers
|
import com.github.damontecres.wholphin.util.WholphinDispatchers
|
||||||
import com.mayakapps.kache.InMemoryKache
|
import com.mayakapps.kache.InMemoryKache
|
||||||
import dagger.assisted.AssistedFactory
|
import dagger.assisted.AssistedFactory
|
||||||
import dagger.assisted.AssistedInject
|
import dagger.assisted.AssistedInject
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
|
import kotlinx.coroutines.CancellationException
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Job
|
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.isActive
|
import kotlinx.coroutines.isActive
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import kotlinx.coroutines.sync.withLock
|
import kotlinx.coroutines.sync.withLock
|
||||||
import org.jellyfin.sdk.api.client.ApiClient
|
import org.jellyfin.sdk.api.client.ApiClient
|
||||||
import org.jellyfin.sdk.api.client.extensions.itemsApi
|
|
||||||
import org.jellyfin.sdk.api.client.extensions.lyricsApi
|
import org.jellyfin.sdk.api.client.extensions.lyricsApi
|
||||||
import org.jellyfin.sdk.api.client.extensions.userLibraryApi
|
import org.jellyfin.sdk.api.client.extensions.userLibraryApi
|
||||||
import org.jellyfin.sdk.model.api.BaseItemKind
|
import org.jellyfin.sdk.model.api.BaseItemDto
|
||||||
import org.jellyfin.sdk.model.api.ImageType
|
import org.jellyfin.sdk.model.api.ImageType
|
||||||
import org.jellyfin.sdk.model.api.ItemSortBy
|
|
||||||
import org.jellyfin.sdk.model.api.LyricDto
|
import org.jellyfin.sdk.model.api.LyricDto
|
||||||
|
import org.jellyfin.sdk.model.api.request.GetItemsRequest
|
||||||
import org.jellyfin.sdk.model.extensions.ticks
|
import org.jellyfin.sdk.model.extensions.ticks
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import kotlin.time.Duration
|
import kotlin.time.Duration
|
||||||
import kotlin.time.Duration.Companion.milliseconds
|
import kotlin.time.Duration.Companion.milliseconds
|
||||||
import kotlin.time.Duration.Companion.seconds
|
import kotlin.time.Duration.Companion.minutes
|
||||||
|
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
@HiltViewModel(assistedFactory = NowPlayingViewModel.Factory::class)
|
@HiltViewModel(assistedFactory = NowPlayingViewModel.Factory::class)
|
||||||
|
|
@ -99,6 +101,9 @@ class NowPlayingViewModel
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
state.update {
|
||||||
|
it.copy(currentMediaItem = player.currentMediaItem?.localConfiguration?.tag as? AudioItem)
|
||||||
|
}
|
||||||
player.addListener(this)
|
player.addListener(this)
|
||||||
addCloseable {
|
addCloseable {
|
||||||
player.removeListener(this)
|
player.removeListener(this)
|
||||||
|
|
@ -126,62 +131,39 @@ class NowPlayingViewModel
|
||||||
}.join()
|
}.join()
|
||||||
controllerViewState.pulseControls()
|
controllerViewState.pulseControls()
|
||||||
}
|
}
|
||||||
viewModelScope.launchDefault {
|
viewModelScope.launchDefault { backdropService.clearBackdrop() }
|
||||||
backdropService.clearBackdrop()
|
listenForBackdrop()
|
||||||
updateBackdrop(getCurrent())
|
listenForLyrics()
|
||||||
}
|
|
||||||
playbackLoop()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun reportInteraction() {
|
fun reportInteraction() {
|
||||||
controllerViewState.pulseControls()
|
controllerViewState.pulseControls()
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun getCurrent(): AudioItem? {
|
private fun listenForLyrics() {
|
||||||
val mediaItem =
|
|
||||||
onMain {
|
|
||||||
player.currentMediaItemIndex
|
|
||||||
.takeIf { it in 0..<player.mediaItemCount }
|
|
||||||
?.let { player.getMediaItemAt(it) }
|
|
||||||
}
|
|
||||||
return mediaItem?.localConfiguration?.tag as? AudioItem
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun playbackLoop() {
|
|
||||||
viewModelScope.launchDefault {
|
viewModelScope.launchDefault {
|
||||||
while (isActive) {
|
state.map { it.currentMediaItem }.distinctUntilChanged().collectLatest { audio ->
|
||||||
val position = onMain { player.currentPosition }.milliseconds
|
if (audio != null && audio.hasLyrics) {
|
||||||
// Timber.v("playbackLoop: %s", position)
|
val lyrics = fetchAndUpdateLyrics(audio)
|
||||||
getCurrent()?.let { audio ->
|
Timber.d("Got lyrics for %s: %s", audio.id, lyrics != null)
|
||||||
// Timber.v("Got current %s", audio.id)
|
while (lyrics != null && isActive) {
|
||||||
if (audio.hasLyrics) {
|
val position = onMain { player.currentPosition }.milliseconds
|
||||||
val lyrics =
|
val offset = lyrics.metadata.offset?.ticks ?: Duration.ZERO
|
||||||
lyricCache.getOrPut(audio.id) {
|
val lyricPosition = offset + position
|
||||||
// TODO remote lyrics?
|
|
||||||
api.lyricsApi.getLyrics(audio.id).content
|
|
||||||
}
|
|
||||||
val lyricIndex =
|
val lyricIndex =
|
||||||
if (lyrics != null) {
|
lyrics.lyrics
|
||||||
val offset = lyrics.metadata.offset?.ticks ?: Duration.ZERO
|
.indexOfLast {
|
||||||
val lyricPosition = offset + position
|
it.start?.ticks?.let { lyricPosition >= it } == true
|
||||||
lyrics.lyrics
|
}.takeIf { it >= 0 }
|
||||||
.indexOfLast {
|
|
||||||
it.start?.ticks?.let { lyricPosition >= it } == true
|
|
||||||
}.takeIf { it >= 0 }
|
|
||||||
} else {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
// Timber.v("lyricIndex=$lyricIndex")
|
|
||||||
state.update {
|
state.update {
|
||||||
it.copy(
|
it.copy(
|
||||||
lyrics = lyrics,
|
lyrics = lyrics,
|
||||||
currentLyricIndex = lyricIndex,
|
currentLyricIndex = lyricIndex,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
delay(150.milliseconds)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
delay(150)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -192,100 +174,114 @@ class NowPlayingViewModel
|
||||||
) {
|
) {
|
||||||
val audio = mediaItem?.localConfiguration?.tag as? AudioItem
|
val audio = mediaItem?.localConfiguration?.tag as? AudioItem
|
||||||
Timber.v("onMediaItemTransition to %s", audio?.id)
|
Timber.v("onMediaItemTransition to %s", audio?.id)
|
||||||
updateBackdrop(audio)
|
state.update {
|
||||||
viewModelScope.launchDefault {
|
it.copy(
|
||||||
state.update {
|
currentMediaItem = audio,
|
||||||
it.copy(
|
lyrics = null,
|
||||||
lyrics = null,
|
currentLyricIndex = null,
|
||||||
currentLyricIndex = null,
|
)
|
||||||
)
|
|
||||||
}
|
|
||||||
audio?.let { audio ->
|
|
||||||
if (audio.hasLyrics) {
|
|
||||||
val lyrics =
|
|
||||||
lyricCache.getOrPut(audio.id) {
|
|
||||||
// TODO remote lyrics?
|
|
||||||
api.lyricsApi.getLyrics(audio.id).content
|
|
||||||
}
|
|
||||||
Timber.d("Got lyrics for %s: %s", audio.id, lyrics != null)
|
|
||||||
state.update {
|
|
||||||
it.copy(
|
|
||||||
lyrics = lyrics,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private var backDropJob: Job? = null
|
private suspend fun fetchAndUpdateLyrics(audio: AudioItem?): LyricDto? =
|
||||||
|
audio?.let { audio ->
|
||||||
private fun updateBackdrop(audio: AudioItem?) {
|
if (audio.hasLyrics) {
|
||||||
backDropJob?.cancel()
|
lyricCache.getOrPut(audio.id) {
|
||||||
backDropJob =
|
// TODO remote lyrics?
|
||||||
viewModelScope.launchDefault {
|
|
||||||
val showBackdrop =
|
|
||||||
userPreferencesService
|
|
||||||
.getCurrent()
|
|
||||||
.appPreferences.musicPreferences.showBackdrop
|
|
||||||
if (showBackdrop) {
|
|
||||||
var backdropItem: BaseItem? = null
|
|
||||||
try {
|
try {
|
||||||
if (audio?.artistId != null) {
|
api.lyricsApi.getLyrics(audio.id).content
|
||||||
api.userLibraryApi.getItem(audio.artistId).content.let {
|
} catch (ex: CancellationException) {
|
||||||
if (it.backdropImageTags?.isNotEmpty() == true) {
|
throw ex
|
||||||
backdropItem = BaseItem(it, false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (backdropItem == null && audio?.albumId != null) {
|
|
||||||
api.userLibraryApi.getItem(audio.albumId).content.let {
|
|
||||||
backdropItem =
|
|
||||||
getBackdropItemForAlbum(api, BaseItem(it, false))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (backdropItem != null) {
|
|
||||||
doUpdateBackdrop(backdropItem)
|
|
||||||
} else {
|
|
||||||
doUpdateBackdropRandom()
|
|
||||||
}
|
|
||||||
} catch (ex: Exception) {
|
} catch (ex: Exception) {
|
||||||
Timber.e(ex, "Error fetching backdrop")
|
Timber.e(ex, "Error fetching lyrics for %s", audio.id)
|
||||||
doUpdateBackdropRandom()
|
null
|
||||||
}
|
}
|
||||||
delay(60.seconds)
|
|
||||||
doUpdateBackdropRandom()
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun doUpdateBackdropRandom() {
|
private fun listenForBackdrop() {
|
||||||
val randomArtist =
|
viewModelScope.launchDefault {
|
||||||
api.itemsApi
|
userPreferencesService.flow
|
||||||
.getItems(
|
.map { it.appPreferences.musicPreferences.showBackdrop }
|
||||||
recursive = true,
|
.combinePair(state.map { it.currentMediaItem })
|
||||||
imageTypes = listOf(ImageType.BACKDROP),
|
.distinctUntilChanged()
|
||||||
includeItemTypes = listOf(BaseItemKind.MUSIC_ARTIST),
|
.collectLatest { (showBackdrop, audio) ->
|
||||||
sortBy = listOf(ItemSortBy.RANDOM),
|
if (showBackdrop && audio != null) {
|
||||||
limit = 1,
|
try {
|
||||||
).content.items
|
val song =
|
||||||
.firstOrNull()
|
api.userLibraryApi
|
||||||
if (randomArtist != null) {
|
.getItem(audio.id)
|
||||||
doUpdateBackdrop(BaseItem(randomArtist))
|
.content
|
||||||
} else {
|
val songBackdrops = song.backdropImageUrls()
|
||||||
clearBackdrop()
|
|
||||||
|
val artistIds = song.artistItems.orEmpty().map { it.id }
|
||||||
|
val artists =
|
||||||
|
GetItemsRequestHandler
|
||||||
|
.execute(
|
||||||
|
api,
|
||||||
|
GetItemsRequest(
|
||||||
|
ids = artistIds,
|
||||||
|
imageTypes = listOf(ImageType.BACKDROP),
|
||||||
|
limit = 50,
|
||||||
|
enableImages = true,
|
||||||
|
enableImageTypes = listOf(ImageType.BACKDROP),
|
||||||
|
),
|
||||||
|
).content.items
|
||||||
|
val artistBackdrops =
|
||||||
|
artists.flatMap { artist -> artist.backdropImageUrls() }
|
||||||
|
|
||||||
|
val list = songBackdrops.shuffled() + artistBackdrops.shuffled()
|
||||||
|
Timber.v("Got %s backdrops for %s", list.size, song.id)
|
||||||
|
if (list.isNotEmpty()) {
|
||||||
|
val urls = generateSequence { list }.flatten()
|
||||||
|
urls.forEach {
|
||||||
|
val (position, duration) = onMain { player.currentPosition to player.duration }
|
||||||
|
if (duration - position >= 15_000 || state.value.backdropResult == BackdropResult.NONE) {
|
||||||
|
// Only change if there's backdrop currently, or at 15 seconds left to prevent quick changes
|
||||||
|
doUpdateBackdrop(audio.id, it)
|
||||||
|
delay(2.minutes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
clearBackdrop()
|
||||||
|
}
|
||||||
|
} catch (ex: CancellationException) {
|
||||||
|
throw ex
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
Timber.e(ex, "Error fetching backdrops for %s", audio.id)
|
||||||
|
clearBackdrop()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
clearBackdrop()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun doUpdateBackdrop(item: BaseItem) {
|
private fun BaseItemDto.backdropImageUrls() =
|
||||||
val imageUrl = imageUrlService.getItemImageUrl(item, ImageType.BACKDROP)
|
backdropImageTags?.sorted().orEmpty().mapIndexedNotNull { index, tag ->
|
||||||
|
imageUrlService.getItemImageUrl(
|
||||||
|
itemId = id,
|
||||||
|
imageType = ImageType.BACKDROP,
|
||||||
|
tag = tag,
|
||||||
|
imageIndex = index,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun doUpdateBackdrop(
|
||||||
|
itemId: UUID,
|
||||||
|
imageUrl: String,
|
||||||
|
) {
|
||||||
val (primaryColor, secondaryColor, tertiaryColor) =
|
val (primaryColor, secondaryColor, tertiaryColor) =
|
||||||
backdropService.extractColorsFromBackdrop(
|
backdropService.extractColorsFromBackdrop(
|
||||||
imageUrl,
|
imageUrl,
|
||||||
)
|
)
|
||||||
val backdropResult =
|
val backdropResult =
|
||||||
BackdropResult(
|
BackdropResult(
|
||||||
itemId = item.id.toString(),
|
itemId = itemId.toString(),
|
||||||
imageUrl = imageUrl,
|
imageUrl = imageUrl,
|
||||||
primaryColor = primaryColor,
|
primaryColor = primaryColor,
|
||||||
secondaryColor = secondaryColor,
|
secondaryColor = secondaryColor,
|
||||||
|
|
@ -343,19 +339,9 @@ class NowPlayingViewModel
|
||||||
|
|
||||||
fun updatePreferences(prefs: AppPreferences) {
|
fun updatePreferences(prefs: AppPreferences) {
|
||||||
viewModelScope.launchDefault {
|
viewModelScope.launchDefault {
|
||||||
var backdropChanged = false
|
|
||||||
preferencesDataStore.updateData {
|
preferencesDataStore.updateData {
|
||||||
backdropChanged =
|
|
||||||
it.musicPreferences.showBackdrop != prefs.musicPreferences.showBackdrop
|
|
||||||
prefs
|
prefs
|
||||||
}
|
}
|
||||||
if (backdropChanged) {
|
|
||||||
if (prefs.musicPreferences.showBackdrop) {
|
|
||||||
updateBackdrop(getCurrent())
|
|
||||||
} else {
|
|
||||||
clearBackdrop()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue