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.completed(): Result? { 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