Remove Outcome class

This commit is contained in:
vfsfitvnm
2022-07-01 20:19:05 +02:00
parent 17cf2454c7
commit fc9b023174
10 changed files with 172 additions and 423 deletions

View File

@@ -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 <T> Result<T>.recoverIfCancelled(): Result<T>? {
return when (exceptionOrNull()) {
is CancellationException -> null
else -> this
}
}
suspend inline fun <reified T> Outcome<HttpResponse>.bodyCatching(): Outcome<T> {
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<HttpResponse> {
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 <reified T> HttpResponse.bodyCatching(): Outcome<T> {
return runCatching {
Outcome.Success(body<T>())
}.getOrElse { throwable ->
Outcome.Error.Unhandled(throwable)
}
}

View File

@@ -1,72 +0,0 @@
package it.vfsfitvnm.youtubemusic
sealed class Outcome<out T> {
val valueOrNull: T?
get() = when (this) {
is Success -> value
is Recovered -> value
else -> null
}
fun recoverWith(value: @UnsafeVariance T): Outcome<T> {
return when (this) {
is Error -> Recovered(value, this)
else -> this
}
}
inline fun <R> map(block: (T) -> R): Outcome<R> {
return when (this) {
is Success -> Success(block(value))
is Recovered -> Success(block(value))
is Initial -> this
is Loading -> this
is Error -> this
}
}
inline fun <R> flatMap(block: (T) -> Outcome<R>): Outcome<R> {
return when (this) {
is Success -> block(value)
is Recovered -> block(value)
is Initial -> this
is Loading -> this
is Error -> this
}
}
object Initial : Outcome<Nothing>()
object Loading : Outcome<Nothing>()
sealed class Error : Outcome<Nothing>() {
object Network : Error()
class Unhandled(val throwable: Throwable) : Error()
}
class Recovered<T>(val value: T, val error: Error) : Outcome<T>()
class Success<T>(val value: T) : Outcome<T>()
}
fun <T> Outcome<T>?.toNotNull(): Outcome<T?> {
return when (this) {
null -> Outcome.Success(null)
else -> this
}
}
fun <T> Outcome<T?>.toNullable(error: Outcome.Error? = null): Outcome<T>? {
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

View File

@@ -0,0 +1,11 @@
package it.vfsfitvnm.youtubemusic
import io.ktor.utils.io.*
internal fun <T> Result<T>.recoverIfCancelled(): Result<T>? {
return when (exceptionOrNull()) {
is CancellationException -> null
else -> this
}
}

View File

@@ -450,77 +450,79 @@ object YouTube {
}.recoverIfCancelled()
}
suspend fun player(videoId: String, playlistId: String? = null): Outcome<PlayerResponse> {
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<PlayerResponse>? {
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<PlayerResponse>()
}.recoverIfCancelled()
}
private suspend fun getQueue(body: GetQueueBody): Outcome<List<Item.Song>?> {
return client.postCatching("/youtubei/v1/music/get_queue") {
contentType(ContentType.Application.Json)
setBody(body)
parameter("key", Key)
parameter("prettyPrint", false)
}
.bodyCatching<GetQueueResponse>()
.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<List<Item.Song>?>? {
return runCatching {
val body = client.post("/youtubei/v1/music/get_queue") {
contentType(ContentType.Application.Json)
setBody(body)
parameter("key", Key)
parameter("prettyPrint", false)
}.body<GetQueueResponse>()
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<Item.Song?> {
suspend fun song(videoId: String): Result<Item.Song?>? {
return getQueue(
GetQueueBody(
context = Context.DefaultWeb,
videoIds = listOf(videoId),
playlistId = null
)
).map { it?.firstOrNull() }
)?.map { it?.firstOrNull() }
}
suspend fun queue(playlistId: String): Outcome<List<Item.Song>?> {
suspend fun queue(playlistId: String): Result<List<Item.Song>?>? {
return getQueue(
GetQueueBody(
context = Context.DefaultWeb,
@@ -674,7 +676,7 @@ object YouTube {
}
suspend fun browse(browseId: String): Result<BrowseResponse>? {
return runCatching<YouTube, BrowseResponse> {
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<BrowseResponse>()
}.recoverIfCancelled()
}