117 lines
3.9 KiB
Kotlin
117 lines
3.9 KiB
Kotlin
package relax.offline.music.util
|
|
|
|
import android.net.Uri
|
|
import android.os.Build
|
|
import android.text.format.DateUtils
|
|
import androidx.annotation.OptIn
|
|
import androidx.core.net.toUri
|
|
import androidx.core.os.bundleOf
|
|
import androidx.media3.common.MediaItem
|
|
import androidx.media3.common.MediaMetadata
|
|
import androidx.media3.common.util.UnstableApi
|
|
import relax.offline.music.bean.FavoriteBean
|
|
import relax.offline.music.bean.OfflineBean
|
|
import relax.offline.music.innertube.Innertube
|
|
import relax.offline.music.innertube.models.bodies.ContinuationBody
|
|
import relax.offline.music.innertube.requests.playlistPage
|
|
import relax.offline.music.innertube.utils.plus
|
|
|
|
val OfflineBean.asMediaItem: MediaItem
|
|
@OptIn(UnstableApi::class)
|
|
get() = MediaItem.Builder()
|
|
.setMediaId(videoId)
|
|
.setUri(videoId)
|
|
.setCustomCacheKey(videoId)
|
|
.setMediaMetadata(
|
|
MediaMetadata.Builder()
|
|
.setTitle(title)
|
|
.setArtist(name)
|
|
.setArtworkUri(thumbnail?.toUri())
|
|
.build()
|
|
)
|
|
.build()
|
|
|
|
val FavoriteBean.asMediaItem: MediaItem
|
|
@OptIn(UnstableApi::class)
|
|
get() = MediaItem.Builder()
|
|
.setMediaId(videoId)
|
|
.setUri(videoId)
|
|
.setCustomCacheKey(videoId)
|
|
.setMediaMetadata(
|
|
MediaMetadata.Builder()
|
|
.setTitle(title)
|
|
.setArtist(name)
|
|
.setArtworkUri(thumbnail?.toUri())
|
|
.build()
|
|
)
|
|
.build()
|
|
|
|
val Innertube.SongItem.asMediaItem: MediaItem
|
|
@OptIn(UnstableApi::class)
|
|
get() = MediaItem.Builder()
|
|
.setMediaId(key)
|
|
.setUri(key)
|
|
.setCustomCacheKey(key)
|
|
.setMediaMetadata(
|
|
MediaMetadata.Builder()
|
|
.setTitle(info?.name)
|
|
.setArtist(authors?.joinToString("") { it.name ?: "" })
|
|
.setAlbumTitle(album?.name)
|
|
.setArtworkUri(bigThumbnail?.url?.toUri())
|
|
.setExtras(
|
|
bundleOf(
|
|
"albumId" to album?.endpoint?.browseId,
|
|
"durationText" to durationText,
|
|
"artistNames" to authors?.filter { it.endpoint != null }
|
|
?.mapNotNull { it.name },
|
|
"artistIds" to authors?.mapNotNull { it.endpoint?.browseId },
|
|
)
|
|
)
|
|
.build()
|
|
)
|
|
.build()
|
|
|
|
fun String?.thumbnail(size: Int): String? {
|
|
return when {
|
|
this?.startsWith("https://lh3.googleusercontent.com") == true -> "$this-w$size-h$size"
|
|
this?.startsWith("https://yt3.ggpht.com") == true -> "$this-w$size-h$size-s$size"
|
|
else -> this
|
|
}
|
|
}
|
|
|
|
fun Uri?.thumbnail(size: Int): Uri? {
|
|
return toString().thumbnail(size)?.toUri()
|
|
}
|
|
|
|
fun formatAsDuration(millis: Long) = DateUtils.formatElapsedTime(millis / 1000).removePrefix("0")
|
|
|
|
suspend fun Result<Innertube.PlaylistOrAlbumPage>.completed(): Result<Innertube.PlaylistOrAlbumPage>? {
|
|
var playlistPage = getOrNull() ?: return null
|
|
|
|
while (playlistPage.songsPage?.continuation != null) {
|
|
val continuation = playlistPage.songsPage?.continuation!!
|
|
val otherPlaylistPageResult =
|
|
Innertube.playlistPage(ContinuationBody(continuation = continuation)) ?: break
|
|
|
|
if (otherPlaylistPageResult.isFailure) break
|
|
|
|
otherPlaylistPageResult.getOrNull()?.let { otherSongsPage ->
|
|
playlistPage = playlistPage.copy(songsPage = playlistPage.songsPage + otherSongsPage)
|
|
}
|
|
}
|
|
|
|
return Result.success(playlistPage)
|
|
}
|
|
|
|
inline val isAtLeastAndroid6
|
|
get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
|
|
|
|
inline val isAtLeastAndroid8
|
|
get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
|
|
|
|
inline val isAtLeastAndroid12
|
|
get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
|
|
|
|
inline val isAtLeastAndroid13
|
|
get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU
|