Continue removing Outcome class in favor of Result
This commit is contained in:
parent
f7012c9134
commit
21ef7e8d5e
|
@ -28,7 +28,6 @@ import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import coil.compose.AsyncImage
|
import coil.compose.AsyncImage
|
||||||
import com.valentinilk.shimmer.shimmer
|
|
||||||
import it.vfsfitvnm.route.RouteHandler
|
import it.vfsfitvnm.route.RouteHandler
|
||||||
import it.vfsfitvnm.vimusic.Database
|
import it.vfsfitvnm.vimusic.Database
|
||||||
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
|
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
|
||||||
|
@ -62,7 +61,7 @@ fun AlbumScreen(
|
||||||
album?.takeIf {
|
album?.takeIf {
|
||||||
album.thumbnailUrl != null
|
album.thumbnailUrl != null
|
||||||
}?.let(Result.Companion::success) ?: YouTube.playlistOrAlbum(browseId)
|
}?.let(Result.Companion::success) ?: YouTube.playlistOrAlbum(browseId)
|
||||||
.map { youtubeAlbum ->
|
?.map { youtubeAlbum ->
|
||||||
Album(
|
Album(
|
||||||
id = browseId,
|
id = browseId,
|
||||||
title = youtubeAlbum.title,
|
title = youtubeAlbum.title,
|
||||||
|
@ -337,13 +336,9 @@ private fun LoadingOrError(
|
||||||
errorMessage: String? = null,
|
errorMessage: String? = null,
|
||||||
onRetry: (() -> Unit)? = null
|
onRetry: (() -> Unit)? = null
|
||||||
) {
|
) {
|
||||||
val colorPalette = LocalColorPalette.current
|
LoadingOrError(
|
||||||
|
errorMessage = errorMessage,
|
||||||
Box {
|
onRetry = onRetry
|
||||||
Column(
|
|
||||||
modifier = Modifier
|
|
||||||
.alpha(if (errorMessage == null) 1f else 0f)
|
|
||||||
.shimmer()
|
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||||
|
@ -354,7 +349,7 @@ private fun LoadingOrError(
|
||||||
) {
|
) {
|
||||||
Spacer(
|
Spacer(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.background(color = colorPalette.darkGray, shape = ThumbnailRoundness.shape)
|
.background(color = LocalColorPalette.current.darkGray, shape = ThumbnailRoundness.shape)
|
||||||
.size(128.dp)
|
.size(128.dp)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -374,17 +369,4 @@ private fun LoadingOrError(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
errorMessage?.let {
|
|
||||||
TextCard(
|
|
||||||
icon = R.drawable.alert_circle,
|
|
||||||
onClick = onRetry,
|
|
||||||
modifier = Modifier
|
|
||||||
.align(Alignment.Center)
|
|
||||||
) {
|
|
||||||
Title(text = onRetry?.let { "Tap to retry" } ?: "Error")
|
|
||||||
Text(text = "An error has occurred:\n$errorMessage")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,6 @@ import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.zIndex
|
import androidx.compose.ui.zIndex
|
||||||
import coil.compose.AsyncImage
|
import coil.compose.AsyncImage
|
||||||
import com.valentinilk.shimmer.shimmer
|
|
||||||
import it.vfsfitvnm.route.RouteHandler
|
import it.vfsfitvnm.route.RouteHandler
|
||||||
import it.vfsfitvnm.vimusic.Database
|
import it.vfsfitvnm.vimusic.Database
|
||||||
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
|
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
|
||||||
|
@ -35,6 +34,7 @@ import it.vfsfitvnm.vimusic.models.DetailedSong
|
||||||
import it.vfsfitvnm.vimusic.query
|
import it.vfsfitvnm.vimusic.query
|
||||||
import it.vfsfitvnm.vimusic.ui.components.TopAppBar
|
import it.vfsfitvnm.vimusic.ui.components.TopAppBar
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.InHistoryMediaItemMenu
|
import it.vfsfitvnm.vimusic.ui.components.themed.InHistoryMediaItemMenu
|
||||||
|
import it.vfsfitvnm.vimusic.ui.components.themed.LoadingOrError
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.TextCard
|
import it.vfsfitvnm.vimusic.ui.components.themed.TextCard
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.TextPlaceholder
|
import it.vfsfitvnm.vimusic.ui.components.themed.TextPlaceholder
|
||||||
import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette
|
import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette
|
||||||
|
@ -85,7 +85,7 @@ fun ArtistScreen(
|
||||||
artist?.takeIf {
|
artist?.takeIf {
|
||||||
artist.shufflePlaylistId != null
|
artist.shufflePlaylistId != null
|
||||||
}?.let(Result.Companion::success) ?: YouTube.artist(browseId)
|
}?.let(Result.Companion::success) ?: YouTube.artist(browseId)
|
||||||
.map { youtubeArtist ->
|
?.map { youtubeArtist ->
|
||||||
Artist(
|
Artist(
|
||||||
id = browseId,
|
id = browseId,
|
||||||
name = youtubeArtist.name,
|
name = youtubeArtist.name,
|
||||||
|
@ -312,12 +312,10 @@ private fun LoadingOrError(
|
||||||
) {
|
) {
|
||||||
val colorPalette = LocalColorPalette.current
|
val colorPalette = LocalColorPalette.current
|
||||||
|
|
||||||
Box {
|
LoadingOrError(
|
||||||
Column(
|
errorMessage = errorMessage,
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
onRetry = onRetry,
|
||||||
modifier = Modifier
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
.alpha(if (errorMessage == null) 1f else 0f)
|
|
||||||
.shimmer()
|
|
||||||
) {
|
) {
|
||||||
Spacer(
|
Spacer(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
@ -339,17 +337,4 @@ private fun LoadingOrError(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
errorMessage?.let {
|
|
||||||
TextCard(
|
|
||||||
icon = R.drawable.alert_circle,
|
|
||||||
onClick = onRetry,
|
|
||||||
modifier = Modifier
|
|
||||||
.align(Alignment.Center)
|
|
||||||
) {
|
|
||||||
Title(text = onRetry?.let { "Tap to retry" } ?: "Error")
|
|
||||||
Text(text = "An error has occurred:\n$errorMessage")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,6 @@ import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import coil.compose.AsyncImage
|
import coil.compose.AsyncImage
|
||||||
import com.valentinilk.shimmer.shimmer
|
|
||||||
import it.vfsfitvnm.route.RouteHandler
|
import it.vfsfitvnm.route.RouteHandler
|
||||||
import it.vfsfitvnm.vimusic.Database
|
import it.vfsfitvnm.vimusic.Database
|
||||||
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
|
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
|
||||||
|
@ -35,14 +34,12 @@ import it.vfsfitvnm.vimusic.models.Playlist
|
||||||
import it.vfsfitvnm.vimusic.models.SongPlaylistMap
|
import it.vfsfitvnm.vimusic.models.SongPlaylistMap
|
||||||
import it.vfsfitvnm.vimusic.transaction
|
import it.vfsfitvnm.vimusic.transaction
|
||||||
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
|
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
|
||||||
import it.vfsfitvnm.vimusic.ui.components.OutcomeItem
|
|
||||||
import it.vfsfitvnm.vimusic.ui.components.TopAppBar
|
import it.vfsfitvnm.vimusic.ui.components.TopAppBar
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.*
|
import it.vfsfitvnm.vimusic.ui.components.themed.*
|
||||||
import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette
|
import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette
|
||||||
import it.vfsfitvnm.vimusic.ui.styling.LocalTypography
|
import it.vfsfitvnm.vimusic.ui.styling.LocalTypography
|
||||||
import it.vfsfitvnm.vimusic.ui.views.SongItem
|
import it.vfsfitvnm.vimusic.ui.views.SongItem
|
||||||
import it.vfsfitvnm.vimusic.utils.*
|
import it.vfsfitvnm.vimusic.utils.*
|
||||||
import it.vfsfitvnm.youtubemusic.Outcome
|
|
||||||
import it.vfsfitvnm.youtubemusic.YouTube
|
import it.vfsfitvnm.youtubemusic.YouTube
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
@ -92,13 +89,13 @@ fun PlaylistScreen(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var playlistOrAlbum by remember {
|
var playlist by remember {
|
||||||
mutableStateOf<Outcome<YouTube.PlaylistOrAlbum>>(Outcome.Loading)
|
mutableStateOf<Result<YouTube.PlaylistOrAlbum>?>(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
val onLoad = relaunchableEffect(Unit) {
|
val onLoad = relaunchableEffect(Unit) {
|
||||||
playlistOrAlbum = withContext(Dispatchers.IO) {
|
playlist = withContext(Dispatchers.IO) {
|
||||||
YouTube.playlistOrAlbum2(browseId)
|
YouTube.playlistOrAlbum(browseId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,7 +137,7 @@ fun PlaylistScreen(
|
||||||
text = "Enqueue",
|
text = "Enqueue",
|
||||||
onClick = {
|
onClick = {
|
||||||
menuState.hide()
|
menuState.hide()
|
||||||
playlistOrAlbum.valueOrNull?.let { album ->
|
playlist?.getOrNull()?.let { album ->
|
||||||
album.items
|
album.items
|
||||||
?.mapNotNull { song ->
|
?.mapNotNull { song ->
|
||||||
song.toMediaItem(browseId, album)
|
song.toMediaItem(browseId, album)
|
||||||
|
@ -160,7 +157,7 @@ fun PlaylistScreen(
|
||||||
onClick = {
|
onClick = {
|
||||||
menuState.hide()
|
menuState.hide()
|
||||||
|
|
||||||
playlistOrAlbum.valueOrNull?.let { album ->
|
playlist?.getOrNull()?.let { album ->
|
||||||
transaction {
|
transaction {
|
||||||
val playlistId =
|
val playlistId =
|
||||||
Database.insert(
|
Database.insert(
|
||||||
|
@ -196,7 +193,7 @@ fun PlaylistScreen(
|
||||||
onClick = {
|
onClick = {
|
||||||
menuState.hide()
|
menuState.hide()
|
||||||
|
|
||||||
(playlistOrAlbum.valueOrNull?.url
|
(playlist?.getOrNull()?.url
|
||||||
?: "https://music.youtube.com/playlist?list=${
|
?: "https://music.youtube.com/playlist?list=${
|
||||||
browseId.removePrefix(
|
browseId.removePrefix(
|
||||||
"VL"
|
"VL"
|
||||||
|
@ -227,13 +224,7 @@ fun PlaylistScreen(
|
||||||
}
|
}
|
||||||
|
|
||||||
item {
|
item {
|
||||||
OutcomeItem(
|
playlist?.getOrNull()?.let { playlist ->
|
||||||
outcome = playlistOrAlbum,
|
|
||||||
onRetry = onLoad,
|
|
||||||
onLoading = {
|
|
||||||
Loading()
|
|
||||||
}
|
|
||||||
) { playlistOrAlbum ->
|
|
||||||
Row(
|
Row(
|
||||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
@ -243,7 +234,7 @@ fun PlaylistScreen(
|
||||||
.padding(bottom = 16.dp)
|
.padding(bottom = 16.dp)
|
||||||
) {
|
) {
|
||||||
AsyncImage(
|
AsyncImage(
|
||||||
model = playlistOrAlbum.thumbnail?.size(thumbnailSizePx),
|
model = playlist.thumbnail?.size(thumbnailSizePx),
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
contentScale = ContentScale.Crop,
|
contentScale = ContentScale.Crop,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
@ -258,19 +249,19 @@ fun PlaylistScreen(
|
||||||
) {
|
) {
|
||||||
Column {
|
Column {
|
||||||
BasicText(
|
BasicText(
|
||||||
text = playlistOrAlbum.title ?: "Unknown",
|
text = playlist.title ?: "Unknown",
|
||||||
style = typography.m.semiBold
|
style = typography.m.semiBold
|
||||||
)
|
)
|
||||||
|
|
||||||
BasicText(
|
BasicText(
|
||||||
text = buildString {
|
text = buildString {
|
||||||
val authors =
|
val authors =
|
||||||
playlistOrAlbum.authors?.joinToString("") { it.name }
|
playlist.authors?.joinToString("") { it.name }
|
||||||
append(authors)
|
append(authors)
|
||||||
if (authors?.isNotEmpty() == true && playlistOrAlbum.year != null) {
|
if (authors?.isNotEmpty() == true && playlist.year != null) {
|
||||||
append(" • ")
|
append(" • ")
|
||||||
}
|
}
|
||||||
append(playlistOrAlbum.year)
|
append(playlist.year)
|
||||||
},
|
},
|
||||||
style = typography.xs.secondary.semiBold,
|
style = typography.xs.secondary.semiBold,
|
||||||
maxLines = 2,
|
maxLines = 2,
|
||||||
|
@ -291,10 +282,10 @@ fun PlaylistScreen(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.clickable {
|
.clickable {
|
||||||
binder?.stopRadio()
|
binder?.stopRadio()
|
||||||
playlistOrAlbum.items
|
playlist.items
|
||||||
?.shuffled()
|
?.shuffled()
|
||||||
?.mapNotNull { song ->
|
?.mapNotNull { song ->
|
||||||
song.toMediaItem(browseId, playlistOrAlbum)
|
song.toMediaItem(browseId, playlist)
|
||||||
}
|
}
|
||||||
?.let { mediaItems ->
|
?.let { mediaItems ->
|
||||||
binder?.player?.forcePlayFromBeginning(
|
binder?.player?.forcePlayFromBeginning(
|
||||||
|
@ -318,9 +309,9 @@ fun PlaylistScreen(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.clickable {
|
.clickable {
|
||||||
binder?.stopRadio()
|
binder?.stopRadio()
|
||||||
playlistOrAlbum.items
|
playlist.items
|
||||||
?.mapNotNull { song ->
|
?.mapNotNull { song ->
|
||||||
song.toMediaItem(browseId, playlistOrAlbum)
|
song.toMediaItem(browseId, playlist)
|
||||||
}
|
}
|
||||||
?.let { mediaItems ->
|
?.let { mediaItems ->
|
||||||
binder?.player?.forcePlayFromBeginning(
|
binder?.player?.forcePlayFromBeginning(
|
||||||
|
@ -339,22 +330,27 @@ fun PlaylistScreen(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} ?: playlist?.exceptionOrNull()?.let { throwable ->
|
||||||
|
LoadingOrError(
|
||||||
|
errorMessage = throwable.javaClass.canonicalName,
|
||||||
|
onRetry = onLoad
|
||||||
|
)
|
||||||
|
} ?: LoadingOrError()
|
||||||
}
|
}
|
||||||
|
|
||||||
itemsIndexed(
|
itemsIndexed(
|
||||||
items = playlistOrAlbum.valueOrNull?.items ?: emptyList(),
|
items = playlist?.getOrNull()?.items ?: emptyList(),
|
||||||
contentType = { _, song -> song }
|
contentType = { _, song -> song }
|
||||||
) { index, song ->
|
) { index, song ->
|
||||||
SongItem(
|
SongItem(
|
||||||
title = song.info.name,
|
title = song.info.name,
|
||||||
authors = (song.authors
|
authors = (song.authors
|
||||||
?: playlistOrAlbum.valueOrNull?.authors)?.joinToString("") { it.name },
|
?: playlist?.getOrNull()?.authors)?.joinToString("") { it.name },
|
||||||
durationText = song.durationText,
|
durationText = song.durationText,
|
||||||
onClick = {
|
onClick = {
|
||||||
binder?.stopRadio()
|
binder?.stopRadio()
|
||||||
playlistOrAlbum.valueOrNull?.items?.mapNotNull { song ->
|
playlist?.getOrNull()?.items?.mapNotNull { song ->
|
||||||
song.toMediaItem(browseId, playlistOrAlbum.valueOrNull!!)
|
song.toMediaItem(browseId, playlist?.getOrNull()!!)
|
||||||
}?.let { mediaItems ->
|
}?.let { mediaItems ->
|
||||||
binder?.player?.forcePlayAtIndex(mediaItems, index)
|
binder?.player?.forcePlayAtIndex(mediaItems, index)
|
||||||
}
|
}
|
||||||
|
@ -384,7 +380,7 @@ fun PlaylistScreen(
|
||||||
NonQueuedMediaItemMenu(
|
NonQueuedMediaItemMenu(
|
||||||
mediaItem = song.toMediaItem(
|
mediaItem = song.toMediaItem(
|
||||||
browseId,
|
browseId,
|
||||||
playlistOrAlbum.valueOrNull!!
|
playlist?.getOrNull()!!
|
||||||
)
|
)
|
||||||
?: return@SongItem,
|
?: return@SongItem,
|
||||||
onDismiss = menuState::hide,
|
onDismiss = menuState::hide,
|
||||||
|
@ -397,15 +393,16 @@ fun PlaylistScreen(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun Loading() {
|
private fun LoadingOrError(
|
||||||
|
errorMessage: String? = null,
|
||||||
|
onRetry: (() -> Unit)? = null
|
||||||
|
) {
|
||||||
val colorPalette = LocalColorPalette.current
|
val colorPalette = LocalColorPalette.current
|
||||||
|
|
||||||
Column(
|
LoadingOrError(
|
||||||
modifier = Modifier
|
errorMessage = errorMessage,
|
||||||
.shimmer()
|
onRetry = onRetry
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||||
|
|
|
@ -653,11 +653,11 @@ object YouTube {
|
||||||
class Lyrics(
|
class Lyrics(
|
||||||
val browseId: String?,
|
val browseId: String?,
|
||||||
) {
|
) {
|
||||||
suspend fun text(): Result<String?> {
|
suspend fun text(): Result<String?>? {
|
||||||
return if (browseId == null) {
|
return if (browseId == null) {
|
||||||
Result.success(null)
|
Result.success(null)
|
||||||
} else {
|
} else {
|
||||||
browse2(browseId).map { body ->
|
browse2(browseId)?.map { body ->
|
||||||
body.contents
|
body.contents
|
||||||
.sectionListRenderer
|
.sectionListRenderer
|
||||||
?.contents
|
?.contents
|
||||||
|
@ -689,8 +689,8 @@ object YouTube {
|
||||||
}.bodyCatching()
|
}.bodyCatching()
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun browse2(browseId: String): Result<BrowseResponse> {
|
suspend fun browse2(browseId: String): Result<BrowseResponse>? {
|
||||||
return runCatching {
|
return runCatching<YouTube, BrowseResponse> {
|
||||||
client.post("/youtubei/v1/browse") {
|
client.post("/youtubei/v1/browse") {
|
||||||
contentType(ContentType.Application.Json)
|
contentType(ContentType.Application.Json)
|
||||||
setBody(
|
setBody(
|
||||||
|
@ -702,7 +702,7 @@ object YouTube {
|
||||||
parameter("key", Key)
|
parameter("key", Key)
|
||||||
parameter("prettyPrint", false)
|
parameter("prettyPrint", false)
|
||||||
}.body()
|
}.body()
|
||||||
}
|
}.recoverIfCancelled()
|
||||||
}
|
}
|
||||||
|
|
||||||
open class PlaylistOrAlbum(
|
open class PlaylistOrAlbum(
|
||||||
|
@ -723,115 +723,8 @@ object YouTube {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun playlistOrAlbum(browseId: String): Result<PlaylistOrAlbum> {
|
suspend fun playlistOrAlbum(browseId: String): Result<PlaylistOrAlbum>? {
|
||||||
return browse2(browseId).map { body ->
|
return browse2(browseId)?.map { body ->
|
||||||
PlaylistOrAlbum(
|
|
||||||
title = body
|
|
||||||
.header
|
|
||||||
?.musicDetailHeaderRenderer
|
|
||||||
?.title
|
|
||||||
?.text,
|
|
||||||
thumbnail = body
|
|
||||||
.header
|
|
||||||
?.musicDetailHeaderRenderer
|
|
||||||
?.thumbnail
|
|
||||||
?.musicThumbnailRenderer
|
|
||||||
?.thumbnail
|
|
||||||
?.thumbnails
|
|
||||||
?.firstOrNull(),
|
|
||||||
authors = body
|
|
||||||
.header
|
|
||||||
?.musicDetailHeaderRenderer
|
|
||||||
?.subtitle
|
|
||||||
?.splitBySeparator()
|
|
||||||
?.getOrNull(1)
|
|
||||||
?.map { Info.from(it) },
|
|
||||||
year = body
|
|
||||||
.header
|
|
||||||
?.musicDetailHeaderRenderer
|
|
||||||
?.subtitle
|
|
||||||
?.splitBySeparator()
|
|
||||||
?.getOrNull(2)
|
|
||||||
?.firstOrNull()
|
|
||||||
?.text,
|
|
||||||
items = body
|
|
||||||
.contents
|
|
||||||
.singleColumnBrowseResultsRenderer
|
|
||||||
?.tabs
|
|
||||||
?.firstOrNull()
|
|
||||||
?.tabRenderer
|
|
||||||
?.content
|
|
||||||
?.sectionListRenderer
|
|
||||||
?.contents
|
|
||||||
?.firstOrNull()
|
|
||||||
?.musicShelfRenderer
|
|
||||||
?.contents
|
|
||||||
?.map(MusicShelfRenderer.Content::musicResponsiveListItemRenderer)
|
|
||||||
?.mapNotNull { renderer ->
|
|
||||||
PlaylistOrAlbum.Item(
|
|
||||||
info = renderer
|
|
||||||
.flexColumns
|
|
||||||
.getOrNull(0)
|
|
||||||
?.musicResponsiveListItemFlexColumnRenderer
|
|
||||||
?.text
|
|
||||||
?.runs
|
|
||||||
?.getOrNull(0)
|
|
||||||
?.let { Info.from(it) } ?: return@mapNotNull null,
|
|
||||||
authors = renderer
|
|
||||||
.flexColumns
|
|
||||||
.getOrNull(1)
|
|
||||||
?.musicResponsiveListItemFlexColumnRenderer
|
|
||||||
?.text
|
|
||||||
?.runs
|
|
||||||
?.map { Info.from<NavigationEndpoint.Endpoint.Browse>(it) }
|
|
||||||
?.takeIf { it.isNotEmpty() },
|
|
||||||
durationText = renderer
|
|
||||||
.fixedColumns
|
|
||||||
?.getOrNull(0)
|
|
||||||
?.musicResponsiveListItemFlexColumnRenderer
|
|
||||||
?.text
|
|
||||||
?.runs
|
|
||||||
?.getOrNull(0)
|
|
||||||
?.text,
|
|
||||||
album = renderer
|
|
||||||
.flexColumns
|
|
||||||
.getOrNull(2)
|
|
||||||
?.musicResponsiveListItemFlexColumnRenderer
|
|
||||||
?.text
|
|
||||||
?.runs
|
|
||||||
?.firstOrNull()
|
|
||||||
?.let { Info.from(it) },
|
|
||||||
thumbnail = renderer
|
|
||||||
.thumbnail
|
|
||||||
?.musicThumbnailRenderer
|
|
||||||
?.thumbnail
|
|
||||||
?.thumbnails
|
|
||||||
?.firstOrNull()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
?.filter { it.info.endpoint != null },
|
|
||||||
url = body
|
|
||||||
.microformat
|
|
||||||
?.microformatDataRenderer
|
|
||||||
?.urlCanonical,
|
|
||||||
continuation = body
|
|
||||||
.contents
|
|
||||||
.singleColumnBrowseResultsRenderer
|
|
||||||
?.tabs
|
|
||||||
?.firstOrNull()
|
|
||||||
?.tabRenderer
|
|
||||||
?.content
|
|
||||||
?.sectionListRenderer
|
|
||||||
?.continuations
|
|
||||||
?.firstOrNull()
|
|
||||||
?.nextRadioContinuationData
|
|
||||||
?.continuation
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun playlistOrAlbum2(browseId: String): Outcome<PlaylistOrAlbum> {
|
|
||||||
return browse(browseId).map { body ->
|
|
||||||
PlaylistOrAlbum(
|
PlaylistOrAlbum(
|
||||||
title = body
|
title = body
|
||||||
.header
|
.header
|
||||||
|
@ -945,8 +838,8 @@ object YouTube {
|
||||||
val radioEndpoint: NavigationEndpoint.Endpoint.Watch?
|
val radioEndpoint: NavigationEndpoint.Endpoint.Watch?
|
||||||
)
|
)
|
||||||
|
|
||||||
suspend fun artist(browseId: String): Result<Artist> {
|
suspend fun artist(browseId: String): Result<Artist>? {
|
||||||
return browse2(browseId).map { body ->
|
return browse2(browseId)?.map { body ->
|
||||||
Artist(
|
Artist(
|
||||||
name = body
|
name = body
|
||||||
.header
|
.header
|
||||||
|
|
Loading…
Reference in a new issue