diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/services/PlayerService.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/services/PlayerService.kt index 9c18305..d0ccd55 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/services/PlayerService.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/services/PlayerService.kt @@ -367,7 +367,7 @@ class PlayerService : MediaSessionService(), MediaSession.MediaItemFiller, coroutineScope.launch(Dispatchers.IO) { lastBitmap = Coil.imageLoader(applicationContext).execute( ImageRequest.Builder(applicationContext) - .data("${mediaMetadata.artworkUri}-w${notificationThumbnailSize}-h${notificationThumbnailSize}") + .data(mediaMetadata.artworkUri.thumbnail(notificationThumbnailSize)) .build() ).drawable?.let { lastArtworkUri = mediaMetadata.artworkUri 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 1c49894..a0f51be 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 @@ -13,7 +13,6 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.drawBehind -import androidx.compose.ui.draw.drawWithCache import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.graphicsLayer @@ -102,7 +101,7 @@ fun PlayerView( } ) { AsyncImage( - model = "${player.mediaMetadata.artworkUri}-w$smallThumbnailSize-h$smallThumbnailSize", + model = player.mediaMetadata.artworkUri.thumbnail(smallThumbnailSize), contentDescription = null, contentScale = ContentScale.Crop, modifier = Modifier @@ -220,11 +219,11 @@ fun PlayerView( .align(Alignment.CenterHorizontally) ) { val artworkUri = remember(it) { - player.mediaController.getMediaItemAt(it).mediaMetadata.artworkUri + player.mediaController.getMediaItemAt(it).mediaMetadata.artworkUri.thumbnail(thumbnailSizePx) } AsyncImage( - model = "$artworkUri-w$thumbnailSizePx-h$thumbnailSizePx", + model = artworkUri, contentDescription = null, contentScale = ContentScale.Crop, modifier = Modifier diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/PlaylistPreviewItem.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/PlaylistPreviewItem.kt index e50d2f0..f4ccf2e 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/PlaylistPreviewItem.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/PlaylistPreviewItem.kt @@ -26,6 +26,7 @@ import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette import it.vfsfitvnm.vimusic.ui.styling.LocalTypography import it.vfsfitvnm.vimusic.utils.color import it.vfsfitvnm.vimusic.utils.semiBold +import it.vfsfitvnm.vimusic.utils.thumbnail import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.distinctUntilChanged @@ -54,7 +55,7 @@ fun PlaylistPreviewItem( ) { if (thumbnails.toSet().size == 1) { AsyncImage( - model = "${thumbnails.first()}-w${thumbnailSizePx * 2}-h${thumbnailSizePx * 2}", + model = thumbnails.first().thumbnail(thumbnailSizePx * 2), contentDescription = null, contentScale = ContentScale.Crop, modifier = Modifier @@ -68,7 +69,7 @@ fun PlaylistPreviewItem( Alignment.BottomEnd ).forEachIndexed { index, alignment -> AsyncImage( - model = "${thumbnails.getOrNull(index)}-w$thumbnailSizePx-h$thumbnailSizePx", + model = thumbnails.getOrNull(index).thumbnail(thumbnailSizePx), contentDescription = null, contentScale = ContentScale.Crop, modifier = Modifier diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/SongItem.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/SongItem.kt index 76305d1..69a91cd 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/SongItem.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/SongItem.kt @@ -32,6 +32,7 @@ import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette import it.vfsfitvnm.vimusic.ui.styling.LocalTypography import it.vfsfitvnm.vimusic.utils.secondary import it.vfsfitvnm.vimusic.utils.semiBold +import it.vfsfitvnm.vimusic.utils.thumbnail @ExperimentalAnimationApi @@ -49,7 +50,7 @@ fun SongItem( SongItem( thumbnailModel = ImageRequest.Builder(LocalContext.current) .diskCacheKey(mediaItem.mediaId) - .data("${mediaItem.mediaMetadata.artworkUri}-w$thumbnailSize-h$thumbnailSize") + .data(mediaItem.mediaMetadata.artworkUri.thumbnail(thumbnailSize)) .build(), title = mediaItem.mediaMetadata.title!!.toString(), authors = mediaItem.mediaMetadata.artist.toString(), @@ -75,7 +76,7 @@ fun SongItem( onThumbnailContent: (@Composable BoxScope.() -> Unit)? = null, ) { SongItem( - thumbnailModel = "${song.song.thumbnailUrl}-w$thumbnailSize-h$thumbnailSize", + thumbnailModel = song.song.thumbnailUrl?.thumbnail(thumbnailSize), title = song.song.title, authors = song.authors?.joinToString("") { it.text } ?: "", durationText = song.song.durationText, diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/utils/utils.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/utils/utils.kt index c3f0ec7..f9d7fef 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/utils/utils.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/utils/utils.kt @@ -2,6 +2,7 @@ package it.vfsfitvnm.vimusic.utils import android.content.Context import android.content.Intent +import android.net.Uri import androidx.core.net.toUri import androidx.core.os.bundleOf import androidx.media3.common.MediaItem @@ -171,4 +172,16 @@ fun YouTube.PlaylistOrAlbum.Item.toMediaItem( ) .setMediaId(info.endpoint?.videoId ?: return null) .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-s$size" + else -> this + } +} + +fun Uri?.thumbnail(size: Int): Uri? { + return toString().thumbnail(size)?.toUri() } \ No newline at end of file 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 449a4c3..585d5ca 100644 --- a/youtube-music/src/main/kotlin/it/vfsfitvnm/youtubemusic/YouTube.kt +++ b/youtube-music/src/main/kotlin/it/vfsfitvnm/youtubemusic/YouTube.kt @@ -159,18 +159,32 @@ object YouTube { override fun from(content: MusicShelfRenderer.Content): Song { val (mainRuns, otherRuns) = content.runs + // Possible configurations: + // "song" • author(s) • album • duration + // "song" • author(s) • duration + // author(s) • album • duration + // author(s) • duration + + val album: Info? = otherRuns + .getOrNull(otherRuns.lastIndex - 1) + ?.firstOrNull() + ?.takeIf { run -> + run + .navigationEndpoint + ?.browseEndpoint + ?.type == "MUSIC_PAGE_TYPE_ALBUM" + } + ?.let(Info.Companion::from) + return Song( info = Info.from(mainRuns.first()), authors = otherRuns - .getOrNull(otherRuns.lastIndex - 2) + .getOrNull(otherRuns.lastIndex - if (album == null) 1 else 2) ?.map(Info.Companion::from) ?: emptyList(), - album = otherRuns - .getOrNull(otherRuns.lastIndex - 1) - ?.firstOrNull() - ?.let(Info.Companion::from), + album = album, durationText = otherRuns - .getOrNull(otherRuns.lastIndex) + .lastOrNull() ?.firstOrNull()?.text, thumbnail = content .thumbnail @@ -603,8 +617,13 @@ object YouTube { thumbnail = renderer .thumbnail .thumbnails - .getOrNull(0), - durationText = renderer.lengthText.text + .also { + println(it) + } + .firstOrNull(), + durationText = renderer + .lengthText + .text ) }, lyrics = NextResult.Lyrics( diff --git a/youtube-music/src/main/kotlin/it/vfsfitvnm/youtubemusic/models/NavigationEndpoint.kt b/youtube-music/src/main/kotlin/it/vfsfitvnm/youtubemusic/models/NavigationEndpoint.kt index 57f9c1d..a1751dc 100644 --- a/youtube-music/src/main/kotlin/it/vfsfitvnm/youtubemusic/models/NavigationEndpoint.kt +++ b/youtube-music/src/main/kotlin/it/vfsfitvnm/youtubemusic/models/NavigationEndpoint.kt @@ -56,10 +56,8 @@ data class NavigationEndpoint( val endpoint: Endpoint? get() = watchEndpoint ?: browseEndpoint ?: watchPlaylistEndpoint ?: searchEndpoint - @Serializable sealed class Endpoint { - @Serializable data class Watch( val params: String? = null, @@ -69,6 +67,10 @@ data class NavigationEndpoint( val playlistSetVideoId: String? = null, val watchEndpointMusicSupportedConfigs: WatchEndpointMusicSupportedConfigs? = null, ) : Endpoint() { + val type: String? + get() = watchEndpointMusicSupportedConfigs + ?.watchEndpointMusicConfig + ?.musicVideoType @Serializable data class WatchEndpointMusicSupportedConfigs( @@ -82,7 +84,6 @@ data class NavigationEndpoint( } } - @Serializable data class WatchPlaylist( val params: String?, @@ -96,6 +97,10 @@ data class NavigationEndpoint( val browseId: String, val browseEndpointContextSupportedConfigs: BrowseEndpointContextSupportedConfigs?, ) : Endpoint() { + val type: String? + get() = browseEndpointContextSupportedConfigs + ?.browseEndpointContextMusicConfig + ?.pageType @Serializable data class BrowseEndpointContextSupportedConfigs( diff --git a/youtube-music/src/main/kotlin/it/vfsfitvnm/youtubemusic/models/Runs.kt b/youtube-music/src/main/kotlin/it/vfsfitvnm/youtubemusic/models/Runs.kt index 42a5ebd..000dcb5 100644 --- a/youtube-music/src/main/kotlin/it/vfsfitvnm/youtubemusic/models/Runs.kt +++ b/youtube-music/src/main/kotlin/it/vfsfitvnm/youtubemusic/models/Runs.kt @@ -16,7 +16,11 @@ data class Runs( run.text == " • " -> listOf(index - 1, index + 1) else -> emptyList() } - }.windowed(size = 2, step = 2) { (from, to) -> runs.slice(from..to) } + }.windowed(size = 2, step = 2) { (from, to) -> runs.slice(from..to) }.let { + it.ifEmpty { + listOf(runs) + } + } } @Serializable diff --git a/youtube-music/src/main/kotlin/it/vfsfitvnm/youtubemusic/models/ThumbnailRenderer.kt b/youtube-music/src/main/kotlin/it/vfsfitvnm/youtubemusic/models/ThumbnailRenderer.kt index e086ce7..e6068e8 100644 --- a/youtube-music/src/main/kotlin/it/vfsfitvnm/youtubemusic/models/ThumbnailRenderer.kt +++ b/youtube-music/src/main/kotlin/it/vfsfitvnm/youtubemusic/models/ThumbnailRenderer.kt @@ -45,3 +45,4 @@ data class ThumbnailRenderer( } } } +