From fc9b023174921c4909e203e120e96ab5319461f9 Mon Sep 17 00:00:00 2001 From: vfsfitvnm Date: Fri, 1 Jul 2022 20:19:05 +0200 Subject: [PATCH] Remove Outcome class --- .../vimusic/service/PlayerService.kt | 53 +++---- .../vimusic/ui/components/OutcomeItem.kt | 127 ---------------- .../vimusic/ui/screens/IntentUriScreen.kt | 139 +++++++++--------- .../vimusic/ui/screens/SearchResultScreen.kt | 4 +- .../vfsfitvnm/vimusic/ui/views/LyricsView.kt | 12 +- .../vfsfitvnm/vimusic/ui/views/PlayerView.kt | 10 +- .../it/vfsfitvnm/youtubemusic/Extensions.kt | 49 ------ .../it/vfsfitvnm/youtubemusic/Outcome.kt | 72 --------- .../it/vfsfitvnm/youtubemusic/Result.kt | 11 ++ .../it/vfsfitvnm/youtubemusic/YouTube.kt | 118 +++++++-------- 10 files changed, 172 insertions(+), 423 deletions(-) delete mode 100644 app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/components/OutcomeItem.kt delete mode 100644 youtube-music/src/main/kotlin/it/vfsfitvnm/youtubemusic/Extensions.kt delete mode 100644 youtube-music/src/main/kotlin/it/vfsfitvnm/youtubemusic/Outcome.kt create mode 100644 youtube-music/src/main/kotlin/it/vfsfitvnm/youtubemusic/Result.kt diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/service/PlayerService.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/service/PlayerService.kt index 0b0137a..376c810 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/service/PlayerService.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/service/PlayerService.kt @@ -51,7 +51,7 @@ import it.vfsfitvnm.vimusic.R import it.vfsfitvnm.vimusic.models.QueuedMediaItem import it.vfsfitvnm.vimusic.query import it.vfsfitvnm.vimusic.utils.* -import it.vfsfitvnm.youtubemusic.Outcome +import it.vfsfitvnm.youtubemusic.YouTube import it.vfsfitvnm.youtubemusic.models.NavigationEndpoint import kotlinx.coroutines.* import kotlinx.coroutines.flow.StateFlow @@ -427,9 +427,9 @@ class PlayerService : Service(), Player.Listener, PlaybackStatsListener.Callback ringBuffer.getOrNull(0)?.first -> dataSpec.withUri(ringBuffer.getOrNull(0)!!.second) ringBuffer.getOrNull(1)?.first -> dataSpec.withUri(ringBuffer.getOrNull(1)!!.second) else -> { - val url = runBlocking(Dispatchers.IO) { - it.vfsfitvnm.youtubemusic.YouTube.player(videoId) - }.flatMap { body -> + val urlResult = runBlocking(Dispatchers.IO) { + YouTube.player(videoId) + }?.mapCatching { body -> val loudnessDb = body.playerConfig?.audioConfig?.loudnessDb?.toFloat() songPendingLoudnessDb[videoId] = loudnessDb @@ -462,42 +462,25 @@ class PlayerService : Service(), Player.Listener, PlaybackStatsListener.Callback } } - Outcome.Success(format.url) - } ?: Outcome.Error.Unhandled( - PlaybackException( - "Couldn't find a playable audio format", - null, - PlaybackException.ERROR_CODE_REMOTE_ERROR - ) + format.url + } ?: throw PlaybackException( + "Couldn't find a playable audio format", + null, + PlaybackException.ERROR_CODE_REMOTE_ERROR ) - else -> Outcome.Error.Unhandled( - PlaybackException( - status, - null, - PlaybackException.ERROR_CODE_REMOTE_ERROR - ) + else -> throw PlaybackException( + status, + null, + PlaybackException.ERROR_CODE_REMOTE_ERROR ) } } - when (url) { - is Outcome.Success -> { - ringBuffer.append(videoId to url.value.toUri()) - dataSpec.withUri(url.value.toUri()) - .subrange(dataSpec.uriPositionOffset, chunkLength) - } - is Outcome.Error.Network -> throw PlaybackException( - "Couldn't reach the internet", - null, - PlaybackException.ERROR_CODE_REMOTE_ERROR - ) - is Outcome.Error.Unhandled -> throw url.throwable - else -> throw PlaybackException( - "Unexpected error", - null, - PlaybackException.ERROR_CODE_REMOTE_ERROR - ) - } + urlResult?.getOrThrow()?.let { url -> + ringBuffer.append(videoId to url.toUri()) + dataSpec.withUri(url.toUri()) + .subrange(dataSpec.uriPositionOffset, chunkLength) + } ?: throw PlaybackException(null, null, PlaybackException.ERROR_CODE_REMOTE_ERROR) } } } diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/components/OutcomeItem.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/components/OutcomeItem.kt deleted file mode 100644 index 6ae4020..0000000 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/components/OutcomeItem.kt +++ /dev/null @@ -1,127 +0,0 @@ -package it.vfsfitvnm.vimusic.ui.components - -import androidx.annotation.DrawableRes -import androidx.compose.foundation.Image -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.text.BasicText -import androidx.compose.runtime.Composable -import androidx.compose.runtime.SideEffect -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.ColorFilter -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.unit.dp -import it.vfsfitvnm.vimusic.R -import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette -import it.vfsfitvnm.vimusic.ui.styling.LocalTypography -import it.vfsfitvnm.vimusic.utils.italic -import it.vfsfitvnm.vimusic.utils.medium -import it.vfsfitvnm.vimusic.utils.secondary -import it.vfsfitvnm.youtubemusic.Outcome - -@Composable -fun OutcomeItem( - outcome: Outcome, - onInitialize: (() -> Unit)? = null, - onRetry: (() -> Unit)? = onInitialize, - onUninitialized: @Composable () -> Unit = { - onInitialize?.let { - SideEffect(it) - } - }, - onLoading: @Composable () -> Unit = {}, - onError: @Composable (Outcome.Error) -> Unit = { - Error( - error = it, - onRetry = onRetry, - ) - }, - onSuccess: @Composable (T) -> Unit -) { - when (outcome) { - is Outcome.Initial -> onUninitialized() - is Outcome.Loading -> onLoading() - is Outcome.Error -> onError(outcome) - is Outcome.Recovered -> onError(outcome.error) - is Outcome.Success -> onSuccess(outcome.value) - } -} - -@Composable -fun Error( - error: Outcome.Error, - modifier: Modifier = Modifier, - onRetry: (() -> Unit)? = null -) { - Column( - verticalArrangement = Arrangement.spacedBy( - space = 8.dp, - alignment = Alignment.CenterVertically - ), - horizontalAlignment = Alignment.CenterHorizontally, - modifier = modifier - .fillMaxSize() - .padding(horizontal = 16.dp) - ) { - Image( - painter = painterResource(R.drawable.alert_circle), - contentDescription = null, - colorFilter = ColorFilter.tint(Color(0xFFFC5F5F)), - modifier = Modifier - .padding(horizontal = 16.dp) - .size(48.dp) - ) - BasicText( - text = when (error) { - is Outcome.Error.Network -> "Couldn't reach the Internet" - is Outcome.Error.Unhandled -> (error.throwable.message ?: error.throwable.toString()) - }, - style = LocalTypography.current.xxs.medium.secondary, - ) - - onRetry?.let { retry -> - BasicText( - text = "Retry", - style = LocalTypography.current.xxs.medium, - modifier = Modifier - .clickable(onClick = retry) - .padding(vertical = 8.dp) - .padding(horizontal = 16.dp) - ) - } - - } -} - -@Composable -fun Message( - text: String, - modifier: Modifier = Modifier, - @DrawableRes icon: Int = R.drawable.alert_circle -) { - Column( - verticalArrangement = Arrangement.spacedBy( - space = 8.dp, - alignment = Alignment.CenterVertically - ), - horizontalAlignment = Alignment.CenterHorizontally, - modifier = modifier - .fillMaxSize() - .padding(horizontal = 16.dp) - ) { - Image( - painter = painterResource(icon), - contentDescription = null, - colorFilter = ColorFilter.tint(LocalColorPalette.current.darkGray), - modifier = Modifier - .padding(horizontal = 16.dp) - .size(36.dp) - ) - BasicText( - text = text, - style = LocalTypography.current.xs.medium.secondary.italic, - ) - } -} \ No newline at end of file diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/IntentUriScreen.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/IntentUriScreen.kt index 48f5ca1..98bde65 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/IntentUriScreen.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/IntentUriScreen.kt @@ -17,8 +17,6 @@ import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp -import com.valentinilk.shimmer.ShimmerBounds -import com.valentinilk.shimmer.rememberShimmer import it.vfsfitvnm.route.RouteHandler import it.vfsfitvnm.vimusic.Database import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder @@ -26,19 +24,15 @@ import it.vfsfitvnm.vimusic.R import it.vfsfitvnm.vimusic.models.Playlist import it.vfsfitvnm.vimusic.models.SongPlaylistMap import it.vfsfitvnm.vimusic.transaction -import it.vfsfitvnm.vimusic.ui.components.Error import it.vfsfitvnm.vimusic.ui.components.LocalMenuState -import it.vfsfitvnm.vimusic.ui.components.Message import it.vfsfitvnm.vimusic.ui.components.TopAppBar -import it.vfsfitvnm.vimusic.ui.components.themed.Menu -import it.vfsfitvnm.vimusic.ui.components.themed.MenuCloseButton -import it.vfsfitvnm.vimusic.ui.components.themed.MenuEntry -import it.vfsfitvnm.vimusic.ui.components.themed.TextFieldDialog +import it.vfsfitvnm.vimusic.ui.components.themed.* import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette -import it.vfsfitvnm.vimusic.utils.* -import it.vfsfitvnm.youtubemusic.Outcome +import it.vfsfitvnm.vimusic.utils.asMediaItem +import it.vfsfitvnm.vimusic.utils.enqueue +import it.vfsfitvnm.vimusic.utils.forcePlayAtIndex +import it.vfsfitvnm.vimusic.utils.relaunchableEffect import it.vfsfitvnm.youtubemusic.YouTube -import it.vfsfitvnm.youtubemusic.toNullable import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext @@ -69,19 +63,21 @@ fun IntentUriScreen(uri: Uri) { val density = LocalDensity.current val binder = LocalPlayerServiceBinder.current - var items by remember(uri) { - mutableStateOf>>(Outcome.Loading) + var itemsResult by remember(uri) { + mutableStateOf>?>(null) } val onLoad = relaunchableEffect(uri) { - items = withContext(Dispatchers.IO) { - uri.getQueryParameter("list")?.let { playlistId -> - YouTube.queue(playlistId).toNullable()?.map { songList -> - songList + withContext(Dispatchers.IO) { + itemsResult = uri.getQueryParameter("list")?.let { playlistId -> + YouTube.queue(playlistId)?.map { songList -> + songList ?: emptyList() } } ?: uri.getQueryParameter("v")?.let { videoId -> - YouTube.song(videoId).toNullable()?.map { listOf(it) } - } ?: Outcome.Error.Unhandled(Error("Missing URL parameters")) + YouTube.song(videoId)?.map { song -> + song?.let { listOf(song) } ?: emptyList() + } + } ?: Result.failure(Error("Missing URL parameters")) } } @@ -101,7 +97,8 @@ fun IntentUriScreen(uri: Uri) { transaction { val playlistId = Database.insert(Playlist(name = text)) - items.valueOrNull + itemsResult + ?.getOrNull() ?.map(YouTube.Item.Song::asMediaItem) ?.forEachIndexed { index, mediaItem -> Database.insert(mediaItem) @@ -159,7 +156,8 @@ fun IntentUriScreen(uri: Uri) { onClick = { menuState.hide() - items.valueOrNull + itemsResult + ?.getOrNull() ?.map(YouTube.Item.Song::asMediaItem) ?.let { mediaItems -> binder?.player?.enqueue( @@ -185,59 +183,62 @@ fun IntentUriScreen(uri: Uri) { } } - when (val currentItems = items) { - is Outcome.Error -> item { - Error( - error = currentItems, - onRetry = onLoad, - modifier = Modifier - .padding(vertical = 16.dp) - ) - } - is Outcome.Recovered -> item { - Error( - error = currentItems.error, - onRetry = onLoad, - modifier = Modifier - .padding(vertical = 16.dp) - ) - } - is Outcome.Loading, is Outcome.Initial -> items(count = 5) { index -> - SmallSongItemShimmer( - thumbnailSizeDp = 54.dp, - modifier = Modifier - .alpha(1f - index * 0.175f) - .fillMaxWidth() - .padding(vertical = 4.dp, horizontal = 16.dp) - ) - } - is Outcome.Success -> { - if (currentItems.value.isEmpty()) { - item { - Message( - text = "No songs were found", - modifier = Modifier - ) - } - } else { - itemsIndexed( - items = currentItems.value, - contentType = { _, item -> item } - ) { index, item -> - SmallSongItem( - song = item, - thumbnailSizePx = density.run { 54.dp.roundToPx() }, - onClick = { - binder?.stopRadio() - binder?.player?.forcePlayAtIndex(currentItems.value.map(YouTube.Item.Song::asMediaItem), index) - } - ) + + itemsResult?.getOrNull()?.let { items -> + if (items.isEmpty()) { + item { + TextCard(icon = R.drawable.sad) { + Title(text = "No songs found") + Text(text = "Please try a different query or category.") } } + } else { + itemsIndexed( + items = items, + contentType = { _, item -> item } + ) { index, item -> + SmallSongItem( + song = item, + thumbnailSizePx = density.run { 54.dp.roundToPx() }, + onClick = { + binder?.stopRadio() + binder?.player?.forcePlayAtIndex(items.map(YouTube.Item.Song::asMediaItem), index) + } + ) + } } - else -> {} + } ?: itemsResult?.exceptionOrNull()?.let { throwable -> + item { + LoadingOrError( + errorMessage = throwable.javaClass.canonicalName, + onRetry = onLoad + ) + } + } ?: item { + LoadingOrError() } } } } } + +@Composable +private fun LoadingOrError( + errorMessage: String? = null, + onRetry: (() -> Unit)? = null +) { + LoadingOrError( + errorMessage = errorMessage, + onRetry = onRetry + ) { + repeat(5) { index -> + SmallSongItemShimmer( + thumbnailSizeDp = 54.dp, + modifier = Modifier + .alpha(1f - index * 0.175f) + .fillMaxWidth() + .padding(vertical = 4.dp, horizontal = 16.dp) + ) + } + } +} diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/SearchResultScreen.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/SearchResultScreen.kt index b65ee97..532a689 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/SearchResultScreen.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/SearchResultScreen.kt @@ -250,9 +250,7 @@ fun SearchResultScreen( } ?: continuationResult?.let { if (items.isEmpty()) { item { - TextCard( - icon = R.drawable.sad - ) { + TextCard(icon = R.drawable.sad) { Title(text = "No results found") Text(text = "Please try a different query or category.") } diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/LyricsView.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/LyricsView.kt index 97b1284..d4cc2d0 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/LyricsView.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/LyricsView.kt @@ -16,7 +16,7 @@ import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.unit.dp import com.valentinilk.shimmer.shimmer import it.vfsfitvnm.vimusic.R -import it.vfsfitvnm.vimusic.ui.components.Message +import it.vfsfitvnm.vimusic.ui.components.themed.TextCard import it.vfsfitvnm.vimusic.ui.components.themed.TextFieldDialog import it.vfsfitvnm.vimusic.ui.components.themed.TextPlaceholder import it.vfsfitvnm.vimusic.ui.styling.LocalTypography @@ -64,10 +64,12 @@ fun LyricsView( .padding(horizontal = 48.dp) ) { if (lyrics.isEmpty()) { - Message( - text = "Lyrics not available", - icon = R.drawable.text, - ) + TextCard( + icon = R.drawable.sad + ) { + Title(text = "No results found") + Text(text = "Please try a different query or category.") + } } else { BasicText( text = lyrics, diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/PlayerView.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/PlayerView.kt index 6d42716..10055ef 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/PlayerView.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/PlayerView.kt @@ -43,12 +43,12 @@ import it.vfsfitvnm.vimusic.enums.ThumbnailRoundness import it.vfsfitvnm.vimusic.models.Song import it.vfsfitvnm.vimusic.query import it.vfsfitvnm.vimusic.ui.components.* +import it.vfsfitvnm.vimusic.ui.components.themed.LoadingOrError import it.vfsfitvnm.vimusic.ui.components.themed.QueuedMediaItemMenu import it.vfsfitvnm.vimusic.ui.styling.BlackColorPalette import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette import it.vfsfitvnm.vimusic.ui.styling.LocalTypography import it.vfsfitvnm.vimusic.utils.* -import it.vfsfitvnm.youtubemusic.Outcome import it.vfsfitvnm.youtubemusic.YouTube import it.vfsfitvnm.youtubemusic.models.PlayerResponse import kotlinx.coroutines.Dispatchers @@ -416,7 +416,7 @@ fun PlayerView( coroutineScope.launch(Dispatchers.IO) { YouTube .player(song.id) - .map { body -> + ?.map { body -> Database.update( song.copy( loudnessDb = body.playerConfig?.audioConfig?.loudnessDb?.toFloat(), @@ -450,14 +450,14 @@ fun PlayerView( .padding(horizontal = 32.dp) .size(thumbnailSizeDp) ) { - Error( - error = Outcome.Error.Unhandled(playerState.error!!), + LoadingOrError( + errorMessage = playerState.error?.javaClass?.canonicalName, onRetry = { player?.playWhenReady = true player?.prepare() playerState.error = null } - ) + ) {} } } diff --git a/youtube-music/src/main/kotlin/it/vfsfitvnm/youtubemusic/Extensions.kt b/youtube-music/src/main/kotlin/it/vfsfitvnm/youtubemusic/Extensions.kt deleted file mode 100644 index d558d12..0000000 --- a/youtube-music/src/main/kotlin/it/vfsfitvnm/youtubemusic/Extensions.kt +++ /dev/null @@ -1,49 +0,0 @@ -package it.vfsfitvnm.youtubemusic - -import io.ktor.client.* -import io.ktor.client.call.* -import io.ktor.client.request.* -import io.ktor.client.statement.* -import io.ktor.util.network.* -import io.ktor.utils.io.* - - -fun Result.recoverIfCancelled(): Result? { - return when (exceptionOrNull()) { - is CancellationException -> null - else -> this - } -} - -suspend inline fun Outcome.bodyCatching(): Outcome { - return when (this) { - is Outcome.Success -> value.bodyCatching() - is Outcome.Recovered -> value.bodyCatching() - is Outcome.Initial -> this - is Outcome.Loading -> this - is Outcome.Error -> this - } -} - -suspend inline fun HttpClient.postCatching( - urlString: String, - block: HttpRequestBuilder.() -> Unit = {} -): Outcome { - return runCatching { - Outcome.Success(post(urlString, block)) - }.getOrElse { throwable -> - when (throwable) { - is CancellationException -> Outcome.Loading - is UnresolvedAddressException -> Outcome.Error.Network - else -> Outcome.Error.Unhandled(throwable) - } - } -} - -suspend inline fun HttpResponse.bodyCatching(): Outcome { - return runCatching { - Outcome.Success(body()) - }.getOrElse { throwable -> - Outcome.Error.Unhandled(throwable) - } -} diff --git a/youtube-music/src/main/kotlin/it/vfsfitvnm/youtubemusic/Outcome.kt b/youtube-music/src/main/kotlin/it/vfsfitvnm/youtubemusic/Outcome.kt deleted file mode 100644 index ec515a9..0000000 --- a/youtube-music/src/main/kotlin/it/vfsfitvnm/youtubemusic/Outcome.kt +++ /dev/null @@ -1,72 +0,0 @@ -package it.vfsfitvnm.youtubemusic - - -sealed class Outcome { - val valueOrNull: T? - get() = when (this) { - is Success -> value - is Recovered -> value - else -> null - } - - fun recoverWith(value: @UnsafeVariance T): Outcome { - return when (this) { - is Error -> Recovered(value, this) - else -> this - } - } - - inline fun map(block: (T) -> R): Outcome { - return when (this) { - is Success -> Success(block(value)) - is Recovered -> Success(block(value)) - is Initial -> this - is Loading -> this - is Error -> this - } - } - - inline fun flatMap(block: (T) -> Outcome): Outcome { - return when (this) { - is Success -> block(value) - is Recovered -> block(value) - is Initial -> this - is Loading -> this - is Error -> this - } - } - - object Initial : Outcome() - - object Loading : Outcome() - - sealed class Error : Outcome() { - object Network : Error() - class Unhandled(val throwable: Throwable) : Error() - } - - class Recovered(val value: T, val error: Error) : Outcome() - - class Success(val value: T) : Outcome() -} - -fun Outcome?.toNotNull(): Outcome { - return when (this) { - null -> Outcome.Success(null) - else -> this - } -} - -fun Outcome.toNullable(error: Outcome.Error? = null): Outcome? { - return when (this) { - is Outcome.Success -> value?.let { Outcome.Success(it) } ?: error - is Outcome.Recovered -> value?.let { Outcome.Success(it) } ?: error - is Outcome.Initial -> this - is Outcome.Loading -> this - is Outcome.Error -> this - } -} - -val Outcome<*>.isEvaluable: Boolean - get() = this !is Outcome.Success && this !is Outcome.Loading - diff --git a/youtube-music/src/main/kotlin/it/vfsfitvnm/youtubemusic/Result.kt b/youtube-music/src/main/kotlin/it/vfsfitvnm/youtubemusic/Result.kt new file mode 100644 index 0000000..d6a2f06 --- /dev/null +++ b/youtube-music/src/main/kotlin/it/vfsfitvnm/youtubemusic/Result.kt @@ -0,0 +1,11 @@ +package it.vfsfitvnm.youtubemusic + +import io.ktor.utils.io.* + + +internal fun Result.recoverIfCancelled(): Result? { + return when (exceptionOrNull()) { + is CancellationException -> null + else -> this + } +} diff --git a/youtube-music/src/main/kotlin/it/vfsfitvnm/youtubemusic/YouTube.kt b/youtube-music/src/main/kotlin/it/vfsfitvnm/youtubemusic/YouTube.kt index 787021b..05f8f6b 100644 --- a/youtube-music/src/main/kotlin/it/vfsfitvnm/youtubemusic/YouTube.kt +++ b/youtube-music/src/main/kotlin/it/vfsfitvnm/youtubemusic/YouTube.kt @@ -450,77 +450,79 @@ object YouTube { }.recoverIfCancelled() } - suspend fun player(videoId: String, playlistId: String? = null): Outcome { - return client.postCatching("/youtubei/v1/player") { - contentType(ContentType.Application.Json) - setBody( - PlayerBody( - context = Context.DefaultAndroid, - videoId = videoId, - playlistId = playlistId, + suspend fun player(videoId: String, playlistId: String? = null): Result? { + return runCatching { + client.post("/youtubei/v1/player") { + contentType(ContentType.Application.Json) + setBody( + PlayerBody( + context = Context.DefaultAndroid, + videoId = videoId, + playlistId = playlistId, + ) ) - ) - parameter("key", Key) - parameter("prettyPrint", false) - }.bodyCatching() + parameter("key", Key) + parameter("prettyPrint", false) + }.body() + }.recoverIfCancelled() } - private suspend fun getQueue(body: GetQueueBody): Outcome?> { - return client.postCatching("/youtubei/v1/music/get_queue") { - contentType(ContentType.Application.Json) - setBody(body) - parameter("key", Key) - parameter("prettyPrint", false) - } - .bodyCatching() - .map { body -> - body.queueDatas?.mapNotNull { queueData -> - queueData.content?.playlistPanelVideoRenderer?.let { renderer -> - Item.Song( - info = Info( - name = renderer - .title - ?.text ?: return@let null, - endpoint = renderer - .navigationEndpoint - .watchEndpoint - ), - authors = renderer - .longBylineText - ?.splitBySeparator() - ?.getOrNull(0) - ?.map { Info.from(it) } - ?: emptyList(), - album = renderer - .longBylineText - ?.splitBySeparator() - ?.getOrNull(1) - ?.getOrNull(0) - ?.let { Info.from(it) }, - thumbnail = renderer - .thumbnail - .thumbnails - .getOrNull(0), - durationText = renderer - .lengthText - ?.text - ) - } + private suspend fun getQueue(body: GetQueueBody): Result?>? { + return runCatching { + val body = client.post("/youtubei/v1/music/get_queue") { + contentType(ContentType.Application.Json) + setBody(body) + parameter("key", Key) + parameter("prettyPrint", false) + }.body() + + body.queueDatas?.mapNotNull { queueData -> + queueData.content?.playlistPanelVideoRenderer?.let { renderer -> + Item.Song( + info = Info( + name = renderer + .title + ?.text ?: return@let null, + endpoint = renderer + .navigationEndpoint + .watchEndpoint + ), + authors = renderer + .longBylineText + ?.splitBySeparator() + ?.getOrNull(0) + ?.map { Info.from(it) } + ?: emptyList(), + album = renderer + .longBylineText + ?.splitBySeparator() + ?.getOrNull(1) + ?.getOrNull(0) + ?.let { Info.from(it) }, + thumbnail = renderer + .thumbnail + .thumbnails + .getOrNull(0), + durationText = renderer + .lengthText + ?.text + ) } } + }.recoverIfCancelled() } - suspend fun song(videoId: String): Outcome { + suspend fun song(videoId: String): Result? { return getQueue( GetQueueBody( context = Context.DefaultWeb, videoIds = listOf(videoId), playlistId = null ) - ).map { it?.firstOrNull() } + )?.map { it?.firstOrNull() } } - suspend fun queue(playlistId: String): Outcome?> { + suspend fun queue(playlistId: String): Result?>? { return getQueue( GetQueueBody( context = Context.DefaultWeb, @@ -674,7 +676,7 @@ object YouTube { } suspend fun browse(browseId: String): Result? { - return runCatching { + return runCatching { client.post("/youtubei/v1/browse") { contentType(ContentType.Application.Json) setBody( @@ -685,7 +687,7 @@ object YouTube { ) parameter("key", Key) parameter("prettyPrint", false) - }.body() + }.body() }.recoverIfCancelled() }