Skip to content

Commit

Permalink
Merge pull request #5557 from aneesh1122/master
Browse files Browse the repository at this point in the history
Improved song matching to include explicit icon and some changes to add to playlist action bar button
  • Loading branch information
fast4x authored Jan 31, 2025
2 parents be29b48 + d1ee774 commit 93fa529
Show file tree
Hide file tree
Showing 7 changed files with 315 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,10 @@ interface Database {
@Query("SELECT playlistId, position FROM SongPlaylistMap WHERE songId = :id")
fun playlistsUsedForSong(id: String): List<PlayListIdPosition>

@Transaction
@Query("SELECT position FROM SongPlaylistMap WHERE playlistId = :playlistId AND songId = :id")
fun positionInPlaylist(id: String, playlistId: Long): Int

@Query("SELECT COUNT(1) FROM Song WHERE likedAt IS NOT NULL")
fun likedSongsCount(): Flow<Int>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2017,7 +2017,7 @@ fun SongMatchingDialog(
Database.asyncTransaction {
deleteSongFromPlaylist(songToRematch.id, playlistId)
if (songExist(song.asSong.id) == 0) {
Database.insert(song.asSong)
Database.insert(song.asMediaItem)
}
insert(
SongPlaylistMap(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.layout.onPlaced
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalUriHandler
Expand All @@ -54,6 +55,7 @@ import androidx.compose.ui.text.ExperimentalTextApi
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.times
import androidx.media3.common.MediaItem
import androidx.media3.common.util.UnstableApi
import androidx.media3.exoplayer.offline.Download
Expand Down Expand Up @@ -1692,3 +1694,169 @@ fun MediaItemMenu(
}
}
}

@ExperimentalTextApi
@SuppressLint("SuspiciousIndentation")
@UnstableApi
@ExperimentalAnimationApi
@Composable
fun AddToPlaylistItemMenu(
navController: NavController,
onDismiss: () -> Unit,
onAddToPlaylist: ((Playlist, Int) -> Unit),
onRemoveFromPlaylist: ((Playlist) -> Unit),
mediaItem: MediaItem,
onGoToPlaylist: ((Long) -> Unit)? = null,
) {
var isCreatingNewPlaylist by rememberSaveable {
mutableStateOf(false)
}
val configuration = LocalConfiguration.current

val screenHeight = configuration.screenHeightDp.dp

if (isCreatingNewPlaylist) {
InputTextDialog(
onDismiss = { isCreatingNewPlaylist = false },
title = stringResource(R.string.enter_the_playlist_name),
value = "",
placeholder = stringResource(R.string.enter_the_playlist_name),
setValue = { text ->
onDismiss()
onAddToPlaylist(Playlist(name = text), 0)
}
)
}
val sortBy by rememberPreference(playlistSortByKey, PlaylistSortBy.DateAdded)
val sortOrder by rememberPreference(playlistSortOrderKey, SortOrder.Descending)
val playlistPreviews by remember {
Database.playlistPreviews(sortBy, sortOrder)
}.collectAsState(initial = emptyList(), context = Dispatchers.IO)

val playlistIds by remember {
Database.getPlaylistsWithSong(mediaItem.mediaId)
}.collectAsState(initial = emptyList(), context = Dispatchers.IO)

val pinnedPlaylists = playlistPreviews.filter {
it.playlist.name.startsWith(PINNED_PREFIX, 0, true)
}

val unpinnedPlaylists = playlistPreviews.filter {
!it.playlist.name.startsWith(PINNED_PREFIX, 0, true) &&
!it.playlist.name.startsWith(MONTHLY_PREFIX, 0, true)
}

Menu(
modifier = Modifier
.requiredHeight(0.75*screenHeight)
) {
Row(
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.padding(horizontal = 16.dp, vertical = 8.dp)
.fillMaxWidth()
) {
IconButton(
onClick = onDismiss,
icon = R.drawable.chevron_back,
color = colorPalette().textSecondary,
modifier = Modifier
.padding(all = 4.dp)
.size(20.dp)
)

SecondaryTextButton(
text = stringResource(R.string.new_playlist),
onClick = { isCreatingNewPlaylist = true },
alternative = true
)
}

if (pinnedPlaylists.isNotEmpty()) {
BasicText(
text = stringResource(R.string.pinned_playlists),
style = typography().m.semiBold,
modifier = Modifier.padding(start = 20.dp, top = 5.dp)
)

onAddToPlaylist.let { onAddToPlaylist ->
pinnedPlaylists.forEach { playlistPreview ->
MenuEntry(
icon = if (playlistIds.contains(playlistPreview.playlist.id)) R.drawable.checkmark else R.drawable.add_in_playlist,
text = playlistPreview.playlist.name.substringAfter(PINNED_PREFIX),
secondaryText = "${playlistPreview.songCount} " + stringResource(R.string.songs),
onClick = {
if (playlistIds.contains(playlistPreview.playlist.id)){
onRemoveFromPlaylist(playlistPreview.playlist)
} else onAddToPlaylist(playlistPreview.playlist, playlistPreview.songCount)
},
trailingContent = {
IconButton(
icon = R.drawable.open,
color = colorPalette().text,
onClick = {
if (onGoToPlaylist != null) {
onGoToPlaylist(playlistPreview.playlist.id)
onDismiss()
}
navController.navigate(route = "${NavRoutes.localPlaylist.name}/${playlistPreview.playlist.id}")
},
modifier = Modifier
.size(24.dp)
)
}
)
}
}
}

if (unpinnedPlaylists.isNotEmpty()) {
BasicText(
text = stringResource(R.string.playlists),
style = typography().m.semiBold,
modifier = Modifier.padding(start = 20.dp, top = 5.dp)
)

onAddToPlaylist.let { onAddToPlaylist ->
unpinnedPlaylists.forEach { playlistPreview ->
MenuEntry(
icon = if (playlistIds.contains(playlistPreview.playlist.id)) R.drawable.checkmark else R.drawable.add_in_playlist,
text = cleanPrefix(playlistPreview.playlist.name),
secondaryText = "${playlistPreview.songCount} " + stringResource(R.string.songs),
onClick = {
if (playlistIds.contains(playlistPreview.playlist.id)){
onRemoveFromPlaylist(playlistPreview.playlist)
} else onAddToPlaylist(playlistPreview.playlist, playlistPreview.songCount)
},
trailingContent = {
if (playlistPreview.playlist.name.startsWith(PIPED_PREFIX, 0, true))
Image(
painter = painterResource(R.drawable.piped_logo),
contentDescription = null,
colorFilter = ColorFilter.tint(colorPalette().red),
modifier = Modifier
.size(18.dp)
)

IconButton(
icon = R.drawable.open,
color = colorPalette().text,
onClick = {
if (onGoToPlaylist != null) {
onGoToPlaylist(playlistPreview.playlist.id)
onDismiss()
}
navController.navigate(route = "${NavRoutes.localPlaylist.name}/${playlistPreview.playlist.id}")
},
modifier = Modifier
.size(24.dp)
)

}
)
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,40 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.ExperimentalTextApi
import androidx.media3.common.MediaItem
import androidx.media3.common.util.UnstableApi
import androidx.navigation.NavController
import it.fast4x.innertube.YtMusic
import it.fast4x.innertube.models.NavigationEndpoint
import it.fast4x.rimusic.Database
import it.fast4x.rimusic.PIPED_PREFIX
import it.fast4x.rimusic.R
import it.fast4x.rimusic.context
import it.fast4x.rimusic.enums.MenuStyle
import it.fast4x.rimusic.models.SongPlaylistMap
import it.fast4x.rimusic.service.MyDownloadHelper
import it.fast4x.rimusic.service.modern.PlayerServiceModern
import it.fast4x.rimusic.ui.screens.settings.isYouTubeSyncEnabled
import it.fast4x.rimusic.utils.addToPipedPlaylist
import it.fast4x.rimusic.utils.asMediaItem
import it.fast4x.rimusic.utils.forcePlay
import it.fast4x.rimusic.utils.getPipedSession
import it.fast4x.rimusic.utils.isPipedEnabledKey
import it.fast4x.rimusic.utils.menuStyleKey
import it.fast4x.rimusic.utils.rememberEqualizerLauncher
import it.fast4x.rimusic.utils.rememberPreference
import it.fast4x.rimusic.utils.removeFromPipedPlaylist
import it.fast4x.rimusic.utils.seamlessPlay
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import timber.log.Timber
import java.util.UUID

@ExperimentalTextApi
@ExperimentalAnimationApi
Expand Down Expand Up @@ -192,3 +207,75 @@ fun MiniPlayerMenu(
}

}

@ExperimentalTextApi
@ExperimentalAnimationApi
@UnstableApi
@Composable
fun AddToPlaylistPlayerMenu(
navController: NavController,
binder: PlayerServiceModern.Binder,
mediaItem: MediaItem,
onDismiss: () -> Unit,
onClosePlayer: () -> Unit,
) {
val isPipedEnabled by rememberPreference(isPipedEnabledKey, false)
val pipedSession = getPipedSession()
val context = LocalContext.current
val coroutineScope = rememberCoroutineScope()
AddToPlaylistItemMenu(
navController = navController,
mediaItem = mediaItem,
onGoToPlaylist = {
onClosePlayer()
},
onAddToPlaylist = { playlist, position ->
Database.asyncTransaction {
insert(mediaItem)
insert(
SongPlaylistMap(
songId = mediaItem.mediaId,
playlistId = insert(playlist).takeIf { it != -1L } ?: playlist.id,
position = position
)
)
}

if(isYouTubeSyncEnabled())
CoroutineScope(Dispatchers.IO).launch {
playlist.browseId?.let { YtMusic.addToPlaylist(it, mediaItem.mediaId) }
}
if (playlist.name.startsWith(PIPED_PREFIX) && isPipedEnabled && pipedSession.token.isNotEmpty()) {
Timber.d("BaseMediaItemMenu onAddToPlaylist mediaItem ${mediaItem.mediaId}")
addToPipedPlaylist(
context = context,
coroutineScope = coroutineScope,
pipedSession = pipedSession.toApiSession(),
id = UUID.fromString(playlist.browseId),
videos = listOf(mediaItem.mediaId)
)
}
},
onRemoveFromPlaylist = { playlist ->
Database.asyncTransaction {
deleteSongFromPlaylist(mediaItem.mediaId,playlist.id)
if (playlist.name.startsWith(PIPED_PREFIX) && isPipedEnabled && pipedSession.token.isNotEmpty()) {
Timber.d("MediaItemMenu InPlaylistMediaItemMenu onRemoveFromPlaylist browseId ${playlist.browseId}")
removeFromPipedPlaylist(
context = context,
coroutineScope = coroutineScope,
pipedSession = pipedSession.toApiSession(),
id = UUID.fromString(playlist.browseId),positionInPlaylist(mediaItem.mediaId,playlist.id)
)
}
}
if(isYouTubeSyncEnabled() && playlist.browseId != null && !playlist.name.startsWith(PIPED_PREFIX))
CoroutineScope(Dispatchers.IO).launch {
playlist.browseId.let { YtMusic.removeFromPlaylist(
it, mediaItem.mediaId
) }
}
},
onDismiss = onDismiss,
)
}
Loading

0 comments on commit 93fa529

Please sign in to comment.