Repository navigation
fix(playlists): sync playlist removals from menus #4097
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
a12528c
111fcd0
1499e51
dbce2db
2d4ba68
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -188,6 +188,8 @@ import com.metrolist.music.extensions.metadata | |
| import com.metrolist.music.extensions.setOffloadEnabled | ||
| import com.metrolist.music.extensions.toEnum | ||
| import com.metrolist.music.extensions.toMediaItem | ||
| import com.metrolist.music.extensions.withoutPlaylistSource | ||
| import com.metrolist.music.extensions.clearPlaylistSource | ||
| import com.metrolist.music.extensions.toPersistQueue | ||
| import com.metrolist.music.extensions.toQueue | ||
| import com.metrolist.music.lyrics.LyricsHelper | ||
|
|
@@ -251,6 +253,7 @@ import kotlinx.coroutines.withContext | |
| import okhttp3.OkHttpClient | ||
| import timber.log.Timber | ||
| import java.io.ObjectInputStream | ||
| import com.metrolist.music.utils.QueueObjectInputStream | ||
| import java.io.ObjectOutputStream | ||
| import java.time.LocalDateTime | ||
| import javax.inject.Inject | ||
|
|
@@ -343,7 +346,8 @@ class MusicService : | |
|
|
||
| private lateinit var audioQuality: com.metrolist.music.constants.AudioQuality | ||
|
|
||
| private var currentQueue: Queue = EmptyQueue | ||
| private val _currentQueue = MutableStateFlow<Queue>(EmptyQueue) | ||
| val currentQueue = _currentQueue.asStateFlow() | ||
| var queueTitle: String? = null | ||
|
|
||
| val currentMediaMetadata = MutableStateFlow<com.metrolist.music.models.MediaMetadata?>(null) | ||
|
|
@@ -611,6 +615,14 @@ class MusicService : | |
|
|
||
| playerInitialized.value = false | ||
|
|
||
| scope.launch { | ||
| syncUtils.playlistRemovalUpdates.collect { update -> | ||
| if (update.removed) { | ||
| update.setVideoId?.let { clearPlaylistSource(update.songId, it, update.browseId) } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // Call startForeground() as early as possible to satisfy the | ||
| // 5-second timeout imposed by Context.startForegroundService(). | ||
| // On some OEMs (e.g. MIUI), even a DataStore read can be slow | ||
|
|
@@ -1190,7 +1202,7 @@ class MusicService : | |
| if (queueFile.exists()) { | ||
| runCatching { | ||
| queueFile.inputStream().use { fis -> | ||
| ObjectInputStream(fis).use { oos -> | ||
| QueueObjectInputStream(fis).use { oos -> | ||
| oos.readObject() as PersistQueue | ||
| } | ||
| } | ||
|
|
@@ -1221,7 +1233,7 @@ class MusicService : | |
| if (automixFile.exists()) { | ||
| runCatching { | ||
| automixFile.inputStream().use { fis -> | ||
| ObjectInputStream(fis).use { oos -> | ||
| QueueObjectInputStream(fis).use { oos -> | ||
| oos.readObject() as PersistQueue | ||
| } | ||
| } | ||
|
|
@@ -1800,7 +1812,7 @@ class MusicService : | |
| return | ||
| } | ||
|
|
||
| currentQueue = queue | ||
| _currentQueue.value = queue | ||
| queueTitle = null | ||
| val persistShuffleAcrossQueues = dataStore.get(PersistentShuffleAcrossQueuesKey, false) | ||
| if (!persistShuffleAcrossQueues && !restoringQueue) { | ||
|
|
@@ -1862,7 +1874,7 @@ class MusicService : | |
| } | ||
|
|
||
| fun adoptQueue(queue: Queue, title: String? = null, initialQueueSize: Int = 0) { | ||
| currentQueue = queue | ||
| _currentQueue.value = queue | ||
| queueTitle = title | ||
| originalQueueSize = initialQueueSize | ||
| } | ||
|
|
@@ -1920,7 +1932,7 @@ class MusicService : | |
| } | ||
| } | ||
|
|
||
| currentQueue = radioQueue | ||
| _currentQueue.value = radioQueue | ||
| } catch (e: Exception) { | ||
| try { | ||
| val nextResult = | ||
|
|
@@ -2173,6 +2185,32 @@ class MusicService : | |
| player.prepare() | ||
| } | ||
|
|
||
| fun clearLocalPlaylistSource(songId: String, playlistId: String, playlistSongMapId: Int) { | ||
| scope.launch(Dispatchers.Main.immediate) { | ||
| if (!playerInitialized.value) return@launch | ||
| player.clearPlaylistSource { metadata -> | ||
| metadata.id == songId && metadata.localPlaylistId == playlistId && | ||
| metadata.playlistSongMapId == playlistSongMapId | ||
| } | ||
| currentMediaMetadata.value = player.currentMetadata | ||
| } | ||
| } | ||
|
|
||
| fun clearPlaylistSource( | ||
| songId: String, | ||
| setVideoId: String, | ||
| playlistBrowseId: String, | ||
| ) { | ||
| scope.launch(Dispatchers.Main.immediate) { | ||
| if (!playerInitialized.value) return@launch | ||
| player.clearPlaylistSource { metadata -> | ||
| metadata.id == songId && metadata.setVideoId == setVideoId && | ||
| metadata.playlistBrowseId?.removePrefix("VL") == playlistBrowseId.removePrefix("VL") | ||
| } | ||
| currentMediaMetadata.value = player.currentMetadata | ||
| } | ||
| } | ||
|
|
||
| fun toggleLibrary() { | ||
| scope.launch { | ||
| val songToToggle = currentSong.first() | ||
|
|
@@ -2605,13 +2643,13 @@ class MusicService : | |
| if (cachedAutoLoadMore && | ||
| reason != Player.MEDIA_ITEM_TRANSITION_REASON_REPEAT && | ||
| player.mediaItemCount - player.currentMediaItemIndex <= 5 && | ||
| currentQueue.hasNextPage() && | ||
| _currentQueue.value.hasNextPage() && | ||
| !(cachedDisableLoadMoreWhenRepeatAll && player.repeatMode == REPEAT_MODE_ALL) | ||
| ) { | ||
| scope.launch(SilentHandler) { | ||
| val mediaItems = | ||
| withContext(Dispatchers.IO) { | ||
| currentQueue | ||
| _currentQueue.value | ||
| .nextPage() | ||
| .filterExplicit(cachedHideExplicit) | ||
| .filterVideoSongs(cachedHideVideoSongs) | ||
|
|
@@ -4100,7 +4138,7 @@ class MusicService : | |
|
|
||
| try { | ||
| val persistQueue = | ||
| currentQueue.toPersistQueue( | ||
| _currentQueue.value.toPersistQueue( | ||
| title = queueTitle, | ||
| items = player.mediaItems.mapNotNull { it.metadata }, | ||
| mediaItemIndex = player.currentMediaItemIndex, | ||
|
|
@@ -4515,10 +4553,13 @@ class MusicService : | |
| if (targetId.isBlank()) return null | ||
| val songs = database.playlistSongs(targetId).first() | ||
| if (songs.isEmpty()) return null | ||
| val playlistName = database.playlist(targetId).first()?.playlist?.name ?: targetTitle | ||
| val playlist = database.playlist(targetId).first()?.playlist | ||
| ListQueue( | ||
| title = playlistName, | ||
| items = songs.map { it.song.toMediaItem() }, | ||
| title = playlist?.name ?: targetTitle, | ||
| items = songs.map { it.toMediaItem(playlist?.browseId) }, | ||
| playlistBrowseId = playlist?.browseId, | ||
| playlistId = playlist?.id, | ||
| playlistIsEditable = playlist?.isEditable == true, | ||
|
Comment on lines
+4560
to
+4562
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
For an editable playlist started from a widget, the new playlist context is attached but Useful? React with 👍 / 👎.
greptile-apps[bot] marked this conversation as resolved.
Resolved
|
||
| ) | ||
| } | ||
|
|
||
|
|
@@ -4529,11 +4570,15 @@ class MusicService : | |
| if (cachedSongs.isNotEmpty()) { | ||
| ListQueue( | ||
| title = cachedPlaylist?.playlist?.name ?: targetTitle, | ||
| items = cachedSongs.map { it.song.toMediaItem() }, | ||
| items = cachedSongs.map { it.toMediaItem(cachedPlaylist?.playlist?.browseId) }, | ||
| playlistBrowseId = cachedPlaylist?.playlist?.browseId, | ||
| playlistId = cachedPlaylist?.playlist?.id, | ||
| playlistIsEditable = cachedPlaylist?.playlist?.isEditable == true, | ||
| ) | ||
| } else { | ||
| YouTubePlaylistQueue( | ||
| playlistId = targetId, | ||
| isEditable = cachedPlaylist?.playlist?.isEditable ?: false, | ||
| playlistTitle = targetTitle, | ||
| ) | ||
| } | ||
|
|
@@ -4618,15 +4663,11 @@ class MusicService : | |
| } | ||
| return@launch | ||
| } | ||
| val items = playlistSongs.map { it.song.toMediaItem() } | ||
| val playlistName = | ||
| val playlist = | ||
| withContext(Dispatchers.IO) { | ||
| database | ||
| .playlist(playlistId) | ||
| .first() | ||
| ?.playlist | ||
| ?.name | ||
| database.playlist(playlistId).first()?.playlist | ||
| } | ||
| val items = playlistSongs.map { it.toMediaItem(playlist?.browseId) } | ||
| withContext(Dispatchers.IO) { | ||
| MusicAlarmScheduler.scheduleFromPreferences(this@MusicService) | ||
| } | ||
|
|
@@ -4648,10 +4689,13 @@ class MusicService : | |
| player.clearMediaItems() | ||
| playQueue( | ||
| ListQueue( | ||
| title = playlistName, | ||
| title = playlist?.name, | ||
| items = alarmItems, | ||
| startIndex = 0, | ||
| position = 0L, | ||
| playlistBrowseId = playlist?.browseId, | ||
| playlistId = playlist?.id, | ||
| playlistIsEditable = playlist?.isEditable == true, | ||
| ), | ||
| playWhenReady = true, | ||
| ) | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.