Support YouTube playlists
This commit is contained in:
@@ -27,7 +27,7 @@ fun <T>ChipGroup(
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||
modifier = Modifier
|
||||
.horizontalScroll(rememberScrollState())
|
||||
.horizontalScroll(rememberScrollState().also { })
|
||||
.then(modifier)
|
||||
) {
|
||||
items.forEach { chipItem ->
|
||||
|
||||
@@ -24,7 +24,7 @@ import it.vfsfitvnm.vimusic.models.Playlist
|
||||
import it.vfsfitvnm.vimusic.models.SongInPlaylist
|
||||
import it.vfsfitvnm.vimusic.models.SongWithInfo
|
||||
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
|
||||
import it.vfsfitvnm.vimusic.ui.screens.rememberAlbumRoute
|
||||
import it.vfsfitvnm.vimusic.ui.screens.rememberPlaylistOrAlbumRoute
|
||||
import it.vfsfitvnm.vimusic.ui.screens.rememberArtistRoute
|
||||
import it.vfsfitvnm.vimusic.ui.screens.rememberCreatePlaylistRoute
|
||||
import it.vfsfitvnm.vimusic.utils.*
|
||||
@@ -203,7 +203,7 @@ fun BaseMediaItemMenu(
|
||||
val context = LocalContext.current
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
|
||||
val albumRoute = rememberAlbumRoute()
|
||||
val albumRoute = rememberPlaylistOrAlbumRoute()
|
||||
val artistRoute = rememberArtistRoute()
|
||||
|
||||
MediaItemMenu(
|
||||
|
||||
@@ -52,12 +52,12 @@ fun ArtistScreen(
|
||||
}
|
||||
}
|
||||
|
||||
val albumRoute = rememberAlbumRoute()
|
||||
val albumRoute = rememberPlaylistOrAlbumRoute()
|
||||
val artistRoute = rememberArtistRoute()
|
||||
|
||||
RouteHandler(listenToGlobalEmitter = true) {
|
||||
albumRoute { browseId ->
|
||||
AlbumScreen(
|
||||
PlaylistOrAlbumScreen(
|
||||
browseId = browseId ?: error("browseId cannot be null")
|
||||
)
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ fun HomeScreen(intentVideoId: String?) {
|
||||
val playlistRoute = rememberLocalPlaylistRoute()
|
||||
val searchRoute = rememberSearchRoute()
|
||||
val searchResultRoute = rememberSearchResultRoute()
|
||||
val albumRoute = rememberAlbumRoute()
|
||||
val albumRoute = rememberPlaylistOrAlbumRoute()
|
||||
val artistRoute = rememberArtistRoute()
|
||||
|
||||
val (route, onRouteChanged) = rememberRoute(intentVideoId?.let { intentVideoRoute })
|
||||
@@ -136,7 +136,7 @@ fun HomeScreen(intentVideoId: String?) {
|
||||
}
|
||||
|
||||
albumRoute { browseId ->
|
||||
AlbumScreen(
|
||||
PlaylistOrAlbumScreen(
|
||||
browseId = browseId ?: error("browseId cannot be null")
|
||||
)
|
||||
}
|
||||
|
||||
@@ -34,12 +34,12 @@ import kotlinx.coroutines.withContext
|
||||
@ExperimentalAnimationApi
|
||||
@Composable
|
||||
fun IntentVideoScreen(videoId: String) {
|
||||
val albumRoute = rememberAlbumRoute()
|
||||
val albumRoute = rememberPlaylistOrAlbumRoute()
|
||||
val artistRoute = rememberArtistRoute()
|
||||
|
||||
RouteHandler(listenToGlobalEmitter = true) {
|
||||
albumRoute { browseId ->
|
||||
AlbumScreen(
|
||||
PlaylistOrAlbumScreen(
|
||||
browseId = browseId ?: error("browseId cannot be null")
|
||||
)
|
||||
}
|
||||
|
||||
@@ -44,27 +44,27 @@ import kotlinx.coroutines.withContext
|
||||
|
||||
@ExperimentalAnimationApi
|
||||
@Composable
|
||||
fun AlbumScreen(
|
||||
fun PlaylistOrAlbumScreen(
|
||||
browseId: String,
|
||||
) {
|
||||
val scrollState = rememberScrollState()
|
||||
|
||||
var album by remember {
|
||||
mutableStateOf<Outcome<YouTube.Album>>(Outcome.Loading)
|
||||
var playlistOrAlbum by remember {
|
||||
mutableStateOf<Outcome<YouTube.PlaylistOrAlbum>>(Outcome.Loading)
|
||||
}
|
||||
|
||||
val onLoad = relaunchableEffect(Unit) {
|
||||
album = withContext(Dispatchers.IO) {
|
||||
YouTube.album(browseId)
|
||||
playlistOrAlbum = withContext(Dispatchers.IO) {
|
||||
YouTube.playlistOrAlbum(browseId)
|
||||
}
|
||||
}
|
||||
|
||||
val albumRoute = rememberAlbumRoute()
|
||||
val albumRoute = rememberPlaylistOrAlbumRoute()
|
||||
val artistRoute = rememberArtistRoute()
|
||||
|
||||
RouteHandler(listenToGlobalEmitter = true) {
|
||||
albumRoute { browseId ->
|
||||
AlbumScreen(
|
||||
PlaylistOrAlbumScreen(
|
||||
browseId = browseId ?: error("browseId cannot be null")
|
||||
)
|
||||
}
|
||||
@@ -88,6 +88,12 @@ fun AlbumScreen(
|
||||
}
|
||||
}
|
||||
|
||||
val (songThumbnailSizeDp, songThumbnailSizePx) = remember {
|
||||
density.run {
|
||||
54.dp to 54.dp.roundToPx()
|
||||
}
|
||||
}
|
||||
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
|
||||
Column(
|
||||
@@ -128,10 +134,16 @@ fun AlbumScreen(
|
||||
enabled = player?.playbackState == Player.STATE_READY,
|
||||
onClick = {
|
||||
menuState.hide()
|
||||
album.valueOrNull?.let { album ->
|
||||
player?.mediaController?.enqueue(album.items.mapNotNull { song ->
|
||||
song.toMediaItem(browseId, album)
|
||||
})
|
||||
playlistOrAlbum.valueOrNull?.let { album ->
|
||||
album.items
|
||||
?.mapNotNull { song ->
|
||||
song.toMediaItem(browseId, album)
|
||||
}
|
||||
?.let { mediaItems ->
|
||||
player?.mediaController?.enqueue(
|
||||
mediaItems
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -142,25 +154,30 @@ fun AlbumScreen(
|
||||
onClick = {
|
||||
menuState.hide()
|
||||
|
||||
album.valueOrNull?.let { album ->
|
||||
playlistOrAlbum.valueOrNull?.let { album ->
|
||||
coroutineScope.launch(Dispatchers.IO) {
|
||||
Database.internal.runInTransaction {
|
||||
val playlistId = Database.insert(Playlist(name = album.title))
|
||||
val playlistId =
|
||||
Database.insert(Playlist(name = album.title ?: "Unknown"))
|
||||
|
||||
album.items.forEachIndexed { index, song ->
|
||||
song.toMediaItem(browseId, album)?.let { mediaItem ->
|
||||
if (Database.song(mediaItem.mediaId) == null) {
|
||||
Database.insert(mediaItem)
|
||||
}
|
||||
album.items?.forEachIndexed { index, song ->
|
||||
song
|
||||
.toMediaItem(browseId, album)
|
||||
?.let { mediaItem ->
|
||||
if (Database.song(mediaItem.mediaId) == null) {
|
||||
Database.insert(
|
||||
mediaItem
|
||||
)
|
||||
}
|
||||
|
||||
Database.insert(
|
||||
SongInPlaylist(
|
||||
songId = mediaItem.mediaId,
|
||||
playlistId = playlistId,
|
||||
position = index
|
||||
Database.insert(
|
||||
SongInPlaylist(
|
||||
songId = mediaItem.mediaId,
|
||||
playlistId = playlistId,
|
||||
position = index
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -176,12 +193,12 @@ fun AlbumScreen(
|
||||
}
|
||||
|
||||
OutcomeItem(
|
||||
outcome = album,
|
||||
outcome = playlistOrAlbum,
|
||||
onRetry = onLoad,
|
||||
onLoading = {
|
||||
Loading()
|
||||
}
|
||||
) { album ->
|
||||
) { playlistOrAlbum ->
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||
modifier = Modifier
|
||||
@@ -191,7 +208,7 @@ fun AlbumScreen(
|
||||
.padding(bottom = 16.dp)
|
||||
) {
|
||||
AsyncImage(
|
||||
model = album.thumbnail.size(thumbnailSizePx),
|
||||
model = playlistOrAlbum.thumbnail?.size(thumbnailSizePx),
|
||||
contentDescription = null,
|
||||
contentScale = ContentScale.FillBounds,
|
||||
modifier = Modifier
|
||||
@@ -206,12 +223,19 @@ fun AlbumScreen(
|
||||
) {
|
||||
Column {
|
||||
BasicText(
|
||||
text = album.title,
|
||||
text = playlistOrAlbum.title ?: "Unknown",
|
||||
style = typography.m.semiBold
|
||||
)
|
||||
|
||||
BasicText(
|
||||
text = "${album.authors.joinToString("") { it.name }} • ${album.year}",
|
||||
text = buildString {
|
||||
val authors = playlistOrAlbum.authors?.joinToString("") { it.name }
|
||||
append(authors)
|
||||
if (authors?.isNotEmpty() == true && playlistOrAlbum.year != null) {
|
||||
append(" • ")
|
||||
}
|
||||
append(playlistOrAlbum.year)
|
||||
},
|
||||
style = typography.xs.secondary.semiBold,
|
||||
maxLines = 2,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
@@ -231,13 +255,19 @@ fun AlbumScreen(
|
||||
modifier = Modifier
|
||||
.clickable {
|
||||
YoutubePlayer.Radio.reset()
|
||||
player?.mediaController?.forcePlayFromBeginning(
|
||||
album.items.shuffled().mapNotNull { song ->
|
||||
song.toMediaItem(browseId, album)
|
||||
})
|
||||
playlistOrAlbum.items
|
||||
?.shuffled()
|
||||
?.mapNotNull { song ->
|
||||
song.toMediaItem(browseId, playlistOrAlbum)
|
||||
}?.let { mediaItems ->
|
||||
player?.mediaController?.forcePlayFromBeginning(mediaItems)
|
||||
}
|
||||
}
|
||||
.shadow(elevation = 2.dp, shape = CircleShape)
|
||||
.background(color = colorPalette.elevatedBackground, shape = CircleShape)
|
||||
.background(
|
||||
color = colorPalette.elevatedBackground,
|
||||
shape = CircleShape
|
||||
)
|
||||
.padding(horizontal = 16.dp, vertical = 16.dp)
|
||||
.size(20.dp)
|
||||
)
|
||||
@@ -249,12 +279,18 @@ fun AlbumScreen(
|
||||
modifier = Modifier
|
||||
.clickable {
|
||||
YoutubePlayer.Radio.reset()
|
||||
player?.mediaController?.forcePlayFromBeginning(album.items.mapNotNull { song ->
|
||||
song.toMediaItem(browseId, album)
|
||||
})
|
||||
|
||||
playlistOrAlbum.items?.mapNotNull { song ->
|
||||
song.toMediaItem(browseId, playlistOrAlbum)
|
||||
}?.let { mediaItems ->
|
||||
player?.mediaController?.forcePlayFromBeginning(mediaItems)
|
||||
}
|
||||
}
|
||||
.shadow(elevation = 2.dp, shape = CircleShape)
|
||||
.background(color = colorPalette.elevatedBackground, shape = CircleShape)
|
||||
.background(
|
||||
color = colorPalette.elevatedBackground,
|
||||
shape = CircleShape
|
||||
)
|
||||
.padding(horizontal = 16.dp, vertical = 16.dp)
|
||||
.size(20.dp)
|
||||
)
|
||||
@@ -262,30 +298,45 @@ fun AlbumScreen(
|
||||
}
|
||||
}
|
||||
|
||||
album.items.forEachIndexed { index, song ->
|
||||
playlistOrAlbum.items?.forEachIndexed { index, song ->
|
||||
SongItem(
|
||||
title = song.info.name,
|
||||
authors = (song.authors ?: album.authors).joinToString("") { it.name },
|
||||
authors = (song.authors ?: playlistOrAlbum.authors)?.joinToString("") { it.name },
|
||||
durationText = song.durationText,
|
||||
onClick = {
|
||||
YoutubePlayer.Radio.reset()
|
||||
player?.mediaController?.forcePlayAtIndex(album.items.mapNotNull { song ->
|
||||
song.toMediaItem(browseId, album)
|
||||
}, index)
|
||||
|
||||
playlistOrAlbum.items?.mapNotNull { song ->
|
||||
song.toMediaItem(browseId, playlistOrAlbum)
|
||||
}?.let { mediaItems ->
|
||||
player?.mediaController?.forcePlayAtIndex(mediaItems, index)
|
||||
}
|
||||
},
|
||||
startContent = {
|
||||
BasicText(
|
||||
text = "${index + 1}",
|
||||
style = typography.xs.secondary.bold.center,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
modifier = Modifier
|
||||
.width(36.dp)
|
||||
)
|
||||
if (song.thumbnail == null) {
|
||||
BasicText(
|
||||
text = "${index + 1}",
|
||||
style = typography.xs.secondary.bold.center,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
modifier = Modifier
|
||||
.width(36.dp)
|
||||
)
|
||||
} else {
|
||||
AsyncImage(
|
||||
model = song.thumbnail!!.size(songThumbnailSizePx),
|
||||
contentDescription = null,
|
||||
contentScale = ContentScale.FillBounds,
|
||||
modifier = Modifier
|
||||
.clip(ThumbnailRoundness.shape)
|
||||
.size(songThumbnailSizeDp)
|
||||
)
|
||||
}
|
||||
},
|
||||
menuContent = {
|
||||
NonQueuedMediaItemMenu(
|
||||
mediaItem = song.toMediaItem(browseId, album) ?: return@SongItem,
|
||||
mediaItem = song.toMediaItem(browseId, playlistOrAlbum)
|
||||
?: return@SongItem,
|
||||
onDismiss = menuState::hide,
|
||||
)
|
||||
}
|
||||
@@ -53,12 +53,12 @@ fun LocalPlaylistScreen(
|
||||
|
||||
val lazyListState = rememberLazyListState()
|
||||
|
||||
val albumRoute = rememberAlbumRoute()
|
||||
val albumRoute = rememberPlaylistOrAlbumRoute()
|
||||
val artistRoute = rememberArtistRoute()
|
||||
|
||||
RouteHandler(listenToGlobalEmitter = true) {
|
||||
albumRoute { browseId ->
|
||||
AlbumScreen(
|
||||
PlaylistOrAlbumScreen(
|
||||
browseId = browseId ?: error("browseId cannot be null")
|
||||
)
|
||||
}
|
||||
|
||||
@@ -89,14 +89,14 @@ fun SearchResultScreen(
|
||||
}
|
||||
}
|
||||
|
||||
val albumRoute = rememberAlbumRoute()
|
||||
val playlistOrAlbumRoute = rememberPlaylistOrAlbumRoute()
|
||||
val artistRoute = rememberArtistRoute()
|
||||
|
||||
RouteHandler(
|
||||
listenToGlobalEmitter = true
|
||||
) {
|
||||
albumRoute { browseId ->
|
||||
AlbumScreen(
|
||||
playlistOrAlbumRoute { browseId ->
|
||||
PlaylistOrAlbumScreen(
|
||||
browseId = browseId ?: "browseId cannot be null"
|
||||
)
|
||||
}
|
||||
@@ -176,6 +176,14 @@ fun SearchResultScreen(
|
||||
text = "Videos",
|
||||
value = YouTube.Item.Video.Filter.value
|
||||
),
|
||||
ChipItem(
|
||||
text = "Playlists",
|
||||
value = YouTube.Item.CommunityPlaylist.Filter.value
|
||||
),
|
||||
ChipItem(
|
||||
text = "Featured playlists",
|
||||
value = YouTube.Item.FeaturedPlaylist.Filter.value
|
||||
),
|
||||
),
|
||||
value = preferences.searchFilter,
|
||||
selectedBackgroundColor = colorPalette.primaryContainer,
|
||||
@@ -198,8 +206,9 @@ fun SearchResultScreen(
|
||||
thumbnailSizePx = thumbnailSizePx,
|
||||
onClick = {
|
||||
when (item) {
|
||||
is YouTube.Item.Album -> albumRoute(item.info.endpoint!!.browseId)
|
||||
is YouTube.Item.Album -> playlistOrAlbumRoute(item.info.endpoint!!.browseId)
|
||||
is YouTube.Item.Artist -> artistRoute(item.info.endpoint!!.browseId)
|
||||
is YouTube.Item.Playlist -> playlistOrAlbumRoute(item.info.endpoint!!.browseId)
|
||||
is YouTube.Item.Song -> {
|
||||
player?.mediaController?.forcePlay(item.asMediaItem)
|
||||
item.info.endpoint?.let {
|
||||
@@ -377,6 +386,18 @@ fun SmallItem(
|
||||
onClick = onClick,
|
||||
modifier = modifier
|
||||
)
|
||||
is YouTube.Item.Playlist -> SmallPlaylistItem(
|
||||
playlist = item,
|
||||
thumbnailSizeDp = thumbnailSizeDp,
|
||||
thumbnailSizePx = thumbnailSizePx,
|
||||
modifier = modifier
|
||||
.clickable(
|
||||
indication = rememberRipple(bounded = true),
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
onClick = onClick
|
||||
)
|
||||
.padding(vertical = 4.dp, horizontal = 16.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -422,6 +443,56 @@ fun SmallVideoItem(
|
||||
)
|
||||
}
|
||||
|
||||
@ExperimentalAnimationApi
|
||||
@Composable
|
||||
fun SmallPlaylistItem(
|
||||
playlist: YouTube.Item.Playlist,
|
||||
thumbnailSizeDp: Dp,
|
||||
thumbnailSizePx: Int,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val typography = LocalTypography.current
|
||||
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
modifier = modifier
|
||||
) {
|
||||
AsyncImage(
|
||||
model = playlist.thumbnail.size(thumbnailSizePx),
|
||||
contentDescription = null,
|
||||
contentScale = ContentScale.FillBounds,
|
||||
modifier = Modifier
|
||||
.clip(ThumbnailRoundness.shape)
|
||||
.size(thumbnailSizeDp)
|
||||
)
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
) {
|
||||
BasicText(
|
||||
text = playlist.info.name,
|
||||
style = typography.xs.semiBold,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
BasicText(
|
||||
text = buildString {
|
||||
append(playlist.channel?.name)
|
||||
if (playlist.channel?.name?.isEmpty() == false && playlist.songCount != null) {
|
||||
append(" • ")
|
||||
}
|
||||
append("${playlist.songCount} songs")
|
||||
},
|
||||
style = typography.xs,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SmallAlbumItem(
|
||||
album: YouTube.Item.Album,
|
||||
|
||||
@@ -85,12 +85,12 @@ fun SearchScreen(
|
||||
}
|
||||
}.collectAsState(initial = null, context = Dispatchers.IO)
|
||||
|
||||
val albumRoute = rememberAlbumRoute()
|
||||
val albumRoute = rememberPlaylistOrAlbumRoute()
|
||||
val artistRoute = rememberArtistRoute()
|
||||
|
||||
RouteHandler(listenToGlobalEmitter = true) {
|
||||
albumRoute { browseId ->
|
||||
AlbumScreen(
|
||||
PlaylistOrAlbumScreen(
|
||||
browseId = browseId ?: error("browseId cannot be null")
|
||||
)
|
||||
}
|
||||
|
||||
@@ -26,14 +26,14 @@ import it.vfsfitvnm.vimusic.utils.semiBold
|
||||
@ExperimentalAnimationApi
|
||||
@Composable
|
||||
fun SettingsScreen() {
|
||||
val albumRoute = rememberAlbumRoute()
|
||||
val albumRoute = rememberPlaylistOrAlbumRoute()
|
||||
val artistRoute = rememberArtistRoute()
|
||||
|
||||
val scrollState = rememberScrollState()
|
||||
|
||||
RouteHandler(listenToGlobalEmitter = true) {
|
||||
albumRoute { browseId ->
|
||||
AlbumScreen(
|
||||
PlaylistOrAlbumScreen(
|
||||
browseId = browseId ?: error("browseId cannot be null")
|
||||
)
|
||||
}
|
||||
|
||||
@@ -18,12 +18,12 @@ fun rememberIntentVideoRoute(intentVideoId: String?): Route1<String?> {
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun rememberAlbumRoute(): Route1<String?> {
|
||||
fun rememberPlaylistOrAlbumRoute(): Route1<String?> {
|
||||
val browseId = rememberSaveable {
|
||||
mutableStateOf<String?>(null)
|
||||
}
|
||||
return remember {
|
||||
Route1("AlbumRoute", browseId)
|
||||
Route1("PlaylistOrAlbumRoute", browseId)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -134,7 +134,7 @@ fun SongItem(
|
||||
@Composable
|
||||
fun SongItem(
|
||||
title: String,
|
||||
authors: String,
|
||||
authors: String?,
|
||||
durationText: String?,
|
||||
onClick: () -> Unit,
|
||||
startContent: @Composable () -> Unit,
|
||||
@@ -175,7 +175,7 @@ fun SongItem(
|
||||
BasicText(
|
||||
text = buildString {
|
||||
append(authors)
|
||||
if (authors.isNotEmpty() && durationText != null) {
|
||||
if (authors?.isNotEmpty() == true && durationText != null) {
|
||||
append(" • ")
|
||||
}
|
||||
append(durationText)
|
||||
|
||||
@@ -144,25 +144,27 @@ val SongWithInfo.asMediaItem: MediaItem
|
||||
.setMediaId(song.id)
|
||||
.build()
|
||||
|
||||
fun YouTube.AlbumItem.toMediaItem(
|
||||
fun YouTube.PlaylistOrAlbum.Item.toMediaItem(
|
||||
albumId: String,
|
||||
album: YouTube.Album
|
||||
playlistOrAlbum: YouTube.PlaylistOrAlbum
|
||||
): MediaItem? {
|
||||
val isFromAlbum = thumbnail == null
|
||||
|
||||
return MediaItem.Builder()
|
||||
.setMediaMetadata(
|
||||
MediaMetadata.Builder()
|
||||
.setTitle(info.name)
|
||||
.setArtist((authors ?: album.authors).joinToString("") { it.name })
|
||||
.setAlbumTitle(album.title)
|
||||
.setArtworkUri(album.thumbnail.url.toUri())
|
||||
.setArtist((authors ?: playlistOrAlbum.authors)?.joinToString("") { it.name })
|
||||
.setAlbumTitle(if (isFromAlbum) playlistOrAlbum.title else album?.name)
|
||||
.setArtworkUri(if (isFromAlbum) playlistOrAlbum.thumbnail?.url?.toUri() else thumbnail?.url?.toUri())
|
||||
.setExtras(
|
||||
bundleOf(
|
||||
"videoId" to info.endpoint?.videoId,
|
||||
"playlistId" to info.endpoint?.playlistId,
|
||||
"albumId" to albumId,
|
||||
"albumId" to (if (isFromAlbum) albumId else album?.endpoint?.browseId),
|
||||
"durationText" to durationText,
|
||||
"artistNames" to (authors ?: album.authors).map { it.name },
|
||||
"artistIds" to (authors ?: album.authors).map { it.endpoint?.browseId }
|
||||
"artistNames" to (authors ?: playlistOrAlbum.authors)?.map { it.name },
|
||||
"artistIds" to (authors ?: playlistOrAlbum.authors)?.map { it.endpoint?.browseId }
|
||||
)
|
||||
)
|
||||
.build()
|
||||
|
||||
Reference in New Issue
Block a user