Drop DetailedSong model
This commit is contained in:
parent
f3995b8c46
commit
33221746fc
17 changed files with 151 additions and 163 deletions
|
@ -38,10 +38,10 @@ import it.vfsfitvnm.vimusic.enums.SongSortBy
|
||||||
import it.vfsfitvnm.vimusic.enums.SortOrder
|
import it.vfsfitvnm.vimusic.enums.SortOrder
|
||||||
import it.vfsfitvnm.vimusic.models.Album
|
import it.vfsfitvnm.vimusic.models.Album
|
||||||
import it.vfsfitvnm.vimusic.models.Artist
|
import it.vfsfitvnm.vimusic.models.Artist
|
||||||
import it.vfsfitvnm.vimusic.models.DetailedSong
|
import it.vfsfitvnm.vimusic.models.SongWithContentLength
|
||||||
import it.vfsfitvnm.vimusic.models.DetailedSongWithContentLength
|
|
||||||
import it.vfsfitvnm.vimusic.models.Event
|
import it.vfsfitvnm.vimusic.models.Event
|
||||||
import it.vfsfitvnm.vimusic.models.Format
|
import it.vfsfitvnm.vimusic.models.Format
|
||||||
|
import it.vfsfitvnm.vimusic.models.Info
|
||||||
import it.vfsfitvnm.vimusic.models.Playlist
|
import it.vfsfitvnm.vimusic.models.Playlist
|
||||||
import it.vfsfitvnm.vimusic.models.PlaylistPreview
|
import it.vfsfitvnm.vimusic.models.PlaylistPreview
|
||||||
import it.vfsfitvnm.vimusic.models.PlaylistWithSongs
|
import it.vfsfitvnm.vimusic.models.PlaylistWithSongs
|
||||||
|
@ -62,34 +62,34 @@ interface Database {
|
||||||
@Transaction
|
@Transaction
|
||||||
@Query("SELECT * FROM Song WHERE totalPlayTimeMs > 0 ORDER BY ROWID ASC")
|
@Query("SELECT * FROM Song WHERE totalPlayTimeMs > 0 ORDER BY ROWID ASC")
|
||||||
@RewriteQueriesToDropUnusedColumns
|
@RewriteQueriesToDropUnusedColumns
|
||||||
fun songsByRowIdAsc(): Flow<List<DetailedSong>>
|
fun songsByRowIdAsc(): Flow<List<Song>>
|
||||||
|
|
||||||
@Transaction
|
@Transaction
|
||||||
@Query("SELECT * FROM Song WHERE totalPlayTimeMs > 0 ORDER BY ROWID DESC")
|
@Query("SELECT * FROM Song WHERE totalPlayTimeMs > 0 ORDER BY ROWID DESC")
|
||||||
@RewriteQueriesToDropUnusedColumns
|
@RewriteQueriesToDropUnusedColumns
|
||||||
fun songsByRowIdDesc(): Flow<List<DetailedSong>>
|
fun songsByRowIdDesc(): Flow<List<Song>>
|
||||||
|
|
||||||
@Transaction
|
@Transaction
|
||||||
@Query("SELECT * FROM Song WHERE totalPlayTimeMs > 0 ORDER BY title ASC")
|
@Query("SELECT * FROM Song WHERE totalPlayTimeMs > 0 ORDER BY title ASC")
|
||||||
@RewriteQueriesToDropUnusedColumns
|
@RewriteQueriesToDropUnusedColumns
|
||||||
fun songsByTitleAsc(): Flow<List<DetailedSong>>
|
fun songsByTitleAsc(): Flow<List<Song>>
|
||||||
|
|
||||||
@Transaction
|
@Transaction
|
||||||
@Query("SELECT * FROM Song WHERE totalPlayTimeMs > 0 ORDER BY title DESC")
|
@Query("SELECT * FROM Song WHERE totalPlayTimeMs > 0 ORDER BY title DESC")
|
||||||
@RewriteQueriesToDropUnusedColumns
|
@RewriteQueriesToDropUnusedColumns
|
||||||
fun songsByTitleDesc(): Flow<List<DetailedSong>>
|
fun songsByTitleDesc(): Flow<List<Song>>
|
||||||
|
|
||||||
@Transaction
|
@Transaction
|
||||||
@Query("SELECT * FROM Song WHERE totalPlayTimeMs > 0 ORDER BY totalPlayTimeMs ASC")
|
@Query("SELECT * FROM Song WHERE totalPlayTimeMs > 0 ORDER BY totalPlayTimeMs ASC")
|
||||||
@RewriteQueriesToDropUnusedColumns
|
@RewriteQueriesToDropUnusedColumns
|
||||||
fun songsByPlayTimeAsc(): Flow<List<DetailedSong>>
|
fun songsByPlayTimeAsc(): Flow<List<Song>>
|
||||||
|
|
||||||
@Transaction
|
@Transaction
|
||||||
@Query("SELECT * FROM Song WHERE totalPlayTimeMs > 0 ORDER BY totalPlayTimeMs DESC")
|
@Query("SELECT * FROM Song WHERE totalPlayTimeMs > 0 ORDER BY totalPlayTimeMs DESC")
|
||||||
@RewriteQueriesToDropUnusedColumns
|
@RewriteQueriesToDropUnusedColumns
|
||||||
fun songsByPlayTimeDesc(): Flow<List<DetailedSong>>
|
fun songsByPlayTimeDesc(): Flow<List<Song>>
|
||||||
|
|
||||||
fun songs(sortBy: SongSortBy, sortOrder: SortOrder): Flow<List<DetailedSong>> {
|
fun songs(sortBy: SongSortBy, sortOrder: SortOrder): Flow<List<Song>> {
|
||||||
return when (sortBy) {
|
return when (sortBy) {
|
||||||
SongSortBy.PlayTime -> when (sortOrder) {
|
SongSortBy.PlayTime -> when (sortOrder) {
|
||||||
SortOrder.Ascending -> songsByPlayTimeAsc()
|
SortOrder.Ascending -> songsByPlayTimeAsc()
|
||||||
|
@ -109,7 +109,7 @@ interface Database {
|
||||||
@Transaction
|
@Transaction
|
||||||
@Query("SELECT * FROM Song WHERE likedAt IS NOT NULL ORDER BY likedAt DESC")
|
@Query("SELECT * FROM Song WHERE likedAt IS NOT NULL ORDER BY likedAt DESC")
|
||||||
@RewriteQueriesToDropUnusedColumns
|
@RewriteQueriesToDropUnusedColumns
|
||||||
fun favorites(): Flow<List<DetailedSong>>
|
fun favorites(): Flow<List<Song>>
|
||||||
|
|
||||||
@Query("SELECT * FROM QueuedMediaItem")
|
@Query("SELECT * FROM QueuedMediaItem")
|
||||||
fun queue(): List<QueuedMediaItem>
|
fun queue(): List<QueuedMediaItem>
|
||||||
|
@ -187,7 +187,7 @@ interface Database {
|
||||||
@Transaction
|
@Transaction
|
||||||
@Query("SELECT * FROM Song JOIN SongAlbumMap ON Song.id = SongAlbumMap.songId WHERE SongAlbumMap.albumId = :albumId AND position IS NOT NULL ORDER BY position")
|
@Query("SELECT * FROM Song JOIN SongAlbumMap ON Song.id = SongAlbumMap.songId WHERE SongAlbumMap.albumId = :albumId AND position IS NOT NULL ORDER BY position")
|
||||||
@RewriteQueriesToDropUnusedColumns
|
@RewriteQueriesToDropUnusedColumns
|
||||||
fun albumSongs(albumId: String): Flow<List<DetailedSong>>
|
fun albumSongs(albumId: String): Flow<List<Song>>
|
||||||
|
|
||||||
@Query("SELECT * FROM Album WHERE bookmarkedAt IS NOT NULL ORDER BY title ASC")
|
@Query("SELECT * FROM Album WHERE bookmarkedAt IS NOT NULL ORDER BY title ASC")
|
||||||
fun albumsByTitleAsc(): Flow<List<Album>>
|
fun albumsByTitleAsc(): Flow<List<Album>>
|
||||||
|
@ -281,15 +281,14 @@ interface Database {
|
||||||
@Transaction
|
@Transaction
|
||||||
@Query("SELECT * FROM Song JOIN SongArtistMap ON Song.id = SongArtistMap.songId WHERE SongArtistMap.artistId = :artistId AND totalPlayTimeMs > 0 ORDER BY Song.ROWID DESC")
|
@Query("SELECT * FROM Song JOIN SongArtistMap ON Song.id = SongArtistMap.songId WHERE SongArtistMap.artistId = :artistId AND totalPlayTimeMs > 0 ORDER BY Song.ROWID DESC")
|
||||||
@RewriteQueriesToDropUnusedColumns
|
@RewriteQueriesToDropUnusedColumns
|
||||||
fun artistSongs(artistId: String): Flow<List<DetailedSong>>
|
fun artistSongs(artistId: String): Flow<List<Song>>
|
||||||
|
|
||||||
@Query("SELECT * FROM Format WHERE songId = :songId")
|
@Query("SELECT * FROM Format WHERE songId = :songId")
|
||||||
fun format(songId: String): Flow<Format?>
|
fun format(songId: String): Flow<Format?>
|
||||||
|
|
||||||
@Transaction
|
@Transaction
|
||||||
@Query("SELECT * FROM Song JOIN Format ON id = songId WHERE contentLength IS NOT NULL AND totalPlayTimeMs > 0 ORDER BY Song.ROWID DESC")
|
@Query("SELECT Song.*, contentLength FROM Song JOIN Format ON id = songId WHERE contentLength IS NOT NULL AND totalPlayTimeMs > 0 ORDER BY Song.ROWID DESC")
|
||||||
@RewriteQueriesToDropUnusedColumns
|
fun songsWithContentLength(): Flow<List<SongWithContentLength>>
|
||||||
fun songsWithContentLength(): Flow<List<DetailedSongWithContentLength>>
|
|
||||||
|
|
||||||
@Query("""
|
@Query("""
|
||||||
UPDATE SongPlaylistMap SET position =
|
UPDATE SongPlaylistMap SET position =
|
||||||
|
@ -312,12 +311,18 @@ interface Database {
|
||||||
fun loudnessDb(songId: String): Flow<Float?>
|
fun loudnessDb(songId: String): Flow<Float?>
|
||||||
|
|
||||||
@Query("SELECT * FROM Song WHERE title LIKE :query OR artistsText LIKE :query")
|
@Query("SELECT * FROM Song WHERE title LIKE :query OR artistsText LIKE :query")
|
||||||
fun search(query: String): Flow<List<DetailedSong>>
|
fun search(query: String): Flow<List<Song>>
|
||||||
|
|
||||||
|
@Query("SELECT albumId AS id, NULL AS name FROM SongAlbumMap WHERE songId = :songId")
|
||||||
|
fun songAlbumInfo(songId: String): Info
|
||||||
|
|
||||||
|
@Query("SELECT id, name FROM Artist LEFT JOIN SongArtistMap ON id = artistId WHERE songId = :songId")
|
||||||
|
fun songArtistInfo(songId: String): List<Info>
|
||||||
|
|
||||||
@Transaction
|
@Transaction
|
||||||
@Query("SELECT Song.* FROM Event JOIN Song ON Song.id = songId GROUP BY songId ORDER BY SUM(CAST(playTime AS REAL) / (((:now - timestamp) / 86400000) + 1)) DESC LIMIT 1")
|
@Query("SELECT Song.* FROM Event JOIN Song ON Song.id = songId GROUP BY songId ORDER BY SUM(CAST(playTime AS REAL) / (((:now - timestamp) / 86400000) + 1)) DESC LIMIT 1")
|
||||||
@RewriteQueriesToDropUnusedColumns
|
@RewriteQueriesToDropUnusedColumns
|
||||||
fun trending(now: Long = System.currentTimeMillis()): Flow<DetailedSong?>
|
fun trending(now: Long = System.currentTimeMillis()): Flow<Song?>
|
||||||
|
|
||||||
@Query("SELECT COUNT (*) FROM Event")
|
@Query("SELECT COUNT (*) FROM Event")
|
||||||
fun eventsCount(): Flow<Int>
|
fun eventsCount(): Flow<Int>
|
||||||
|
|
|
@ -1,47 +0,0 @@
|
||||||
package it.vfsfitvnm.vimusic.models
|
|
||||||
|
|
||||||
import androidx.compose.runtime.Immutable
|
|
||||||
import androidx.room.Junction
|
|
||||||
import androidx.room.Relation
|
|
||||||
|
|
||||||
@Immutable
|
|
||||||
open class DetailedSong(
|
|
||||||
val id: String,
|
|
||||||
val title: String,
|
|
||||||
val artistsText: String? = null,
|
|
||||||
val durationText: String?,
|
|
||||||
val thumbnailUrl: String?,
|
|
||||||
val totalPlayTimeMs: Long = 0,
|
|
||||||
@Relation(
|
|
||||||
entity = SongAlbumMap::class,
|
|
||||||
entityColumn = "songId",
|
|
||||||
parentColumn = "id",
|
|
||||||
projection = ["albumId"]
|
|
||||||
)
|
|
||||||
val albumId: String?,
|
|
||||||
@Relation(
|
|
||||||
entity = Artist::class,
|
|
||||||
entityColumn = "id",
|
|
||||||
parentColumn = "id",
|
|
||||||
associateBy = Junction(
|
|
||||||
value = SongArtistMap::class,
|
|
||||||
parentColumn = "songId",
|
|
||||||
entityColumn = "artistId"
|
|
||||||
),
|
|
||||||
projection = ["id", "name"]
|
|
||||||
)
|
|
||||||
val artists: List<Info>?
|
|
||||||
) {
|
|
||||||
val formattedTotalPlayTime: String
|
|
||||||
get() {
|
|
||||||
val seconds = totalPlayTimeMs / 1000
|
|
||||||
|
|
||||||
val hours = seconds / 3600
|
|
||||||
|
|
||||||
return when {
|
|
||||||
hours == 0L -> "${seconds / 60}m"
|
|
||||||
hours < 24L -> "${hours}h"
|
|
||||||
else -> "${hours / 24}d"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
package it.vfsfitvnm.vimusic.models
|
|
||||||
|
|
||||||
import androidx.compose.runtime.Immutable
|
|
||||||
import androidx.room.Relation
|
|
||||||
|
|
||||||
@Immutable
|
|
||||||
class DetailedSongWithContentLength(
|
|
||||||
id: String,
|
|
||||||
title: String,
|
|
||||||
artistsText: String? = null,
|
|
||||||
durationText: String?,
|
|
||||||
thumbnailUrl: String?,
|
|
||||||
totalPlayTimeMs: Long = 0,
|
|
||||||
albumId: String?,
|
|
||||||
artists: List<Info>?,
|
|
||||||
@Relation(
|
|
||||||
entity = Format::class,
|
|
||||||
entityColumn = "songId",
|
|
||||||
parentColumn = "id",
|
|
||||||
projection = ["contentLength"]
|
|
||||||
)
|
|
||||||
val contentLength: Long?
|
|
||||||
) : DetailedSong(id, title, artistsText, durationText, thumbnailUrl, totalPlayTimeMs, albumId, artists)
|
|
|
@ -18,5 +18,5 @@ data class PlaylistWithSongs(
|
||||||
entityColumn = "songId"
|
entityColumn = "songId"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
val songs: List<DetailedSong>
|
val songs: List<Song>
|
||||||
)
|
)
|
||||||
|
|
|
@ -17,6 +17,19 @@ data class Song(
|
||||||
val likedAt: Long? = null,
|
val likedAt: Long? = null,
|
||||||
val totalPlayTimeMs: Long = 0
|
val totalPlayTimeMs: Long = 0
|
||||||
) {
|
) {
|
||||||
|
val formattedTotalPlayTime: String
|
||||||
|
get() {
|
||||||
|
val seconds = totalPlayTimeMs / 1000
|
||||||
|
|
||||||
|
val hours = seconds / 3600
|
||||||
|
|
||||||
|
return when {
|
||||||
|
hours == 0L -> "${seconds / 60}m"
|
||||||
|
hours < 24L -> "${hours}h"
|
||||||
|
else -> "${hours / 24}d"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun toggleLike(): Song {
|
fun toggleLike(): Song {
|
||||||
return copy(
|
return copy(
|
||||||
likedAt = if (likedAt == null) System.currentTimeMillis() else null
|
likedAt = if (likedAt == null) System.currentTimeMillis() else null
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
package it.vfsfitvnm.vimusic.models
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Immutable
|
||||||
|
import androidx.room.Embedded
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
data class SongWithContentLength(
|
||||||
|
@Embedded val song: Song,
|
||||||
|
val contentLength: Long?
|
||||||
|
)
|
|
@ -20,8 +20,9 @@ import androidx.media3.datasource.cache.Cache
|
||||||
import it.vfsfitvnm.vimusic.Database
|
import it.vfsfitvnm.vimusic.Database
|
||||||
import it.vfsfitvnm.vimusic.R
|
import it.vfsfitvnm.vimusic.R
|
||||||
import it.vfsfitvnm.vimusic.models.Album
|
import it.vfsfitvnm.vimusic.models.Album
|
||||||
import it.vfsfitvnm.vimusic.models.DetailedSong
|
|
||||||
import it.vfsfitvnm.vimusic.models.PlaylistPreview
|
import it.vfsfitvnm.vimusic.models.PlaylistPreview
|
||||||
|
import it.vfsfitvnm.vimusic.models.Song
|
||||||
|
import it.vfsfitvnm.vimusic.models.SongWithContentLength
|
||||||
import it.vfsfitvnm.vimusic.utils.asMediaItem
|
import it.vfsfitvnm.vimusic.utils.asMediaItem
|
||||||
import it.vfsfitvnm.vimusic.utils.forcePlayAtIndex
|
import it.vfsfitvnm.vimusic.utils.forcePlayAtIndex
|
||||||
import it.vfsfitvnm.vimusic.utils.forceSeekToNext
|
import it.vfsfitvnm.vimusic.utils.forceSeekToNext
|
||||||
|
@ -36,7 +37,7 @@ import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
class PlayerMediaBrowserService : MediaBrowserService(), ServiceConnection {
|
class PlayerMediaBrowserService : MediaBrowserService(), ServiceConnection {
|
||||||
private val coroutineScope = CoroutineScope(Dispatchers.IO)
|
private val coroutineScope = CoroutineScope(Dispatchers.IO)
|
||||||
private var lastSongs = emptyList<DetailedSong>()
|
private var lastSongs = emptyList<Song>()
|
||||||
|
|
||||||
private var bound = false
|
private var bound = false
|
||||||
|
|
||||||
|
@ -187,7 +188,7 @@ class PlayerMediaBrowserService : MediaBrowserService(), ServiceConnection {
|
||||||
BrowserMediaItem.FLAG_PLAYABLE
|
BrowserMediaItem.FLAG_PLAYABLE
|
||||||
)
|
)
|
||||||
|
|
||||||
private val DetailedSong.asBrowserMediaItem
|
private val Song.asBrowserMediaItem
|
||||||
inline get() = BrowserMediaItem(
|
inline get() = BrowserMediaItem(
|
||||||
BrowserMediaDescription.Builder()
|
BrowserMediaDescription.Builder()
|
||||||
.setMediaId(MediaId.forSong(id))
|
.setMediaId(MediaId.forSong(id))
|
||||||
|
@ -254,9 +255,10 @@ class PlayerMediaBrowserService : MediaBrowserService(), ServiceConnection {
|
||||||
.first()
|
.first()
|
||||||
.filter { song ->
|
.filter { song ->
|
||||||
song.contentLength?.let {
|
song.contentLength?.let {
|
||||||
cache.isCached(song.id, 0, song.contentLength)
|
cache.isCached(song.song.id, 0, it)
|
||||||
} ?: false
|
} ?: false
|
||||||
}
|
}
|
||||||
|
.map(SongWithContentLength::song)
|
||||||
.shuffled()
|
.shuffled()
|
||||||
|
|
||||||
MediaId.playlists -> data
|
MediaId.playlists -> data
|
||||||
|
@ -273,7 +275,7 @@ class PlayerMediaBrowserService : MediaBrowserService(), ServiceConnection {
|
||||||
?.first()
|
?.first()
|
||||||
|
|
||||||
else -> emptyList()
|
else -> emptyList()
|
||||||
}?.map(DetailedSong::asMediaItem) ?: return@launch
|
}?.map(Song::asMediaItem) ?: return@launch
|
||||||
|
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
player.forcePlayAtIndex(mediaItems, index.coerceIn(0, mediaItems.size))
|
player.forcePlayAtIndex(mediaItems, index.coerceIn(0, mediaItems.size))
|
||||||
|
|
|
@ -25,6 +25,7 @@ import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.foundation.text.BasicText
|
import androidx.compose.foundation.text.BasicText
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
@ -48,7 +49,7 @@ import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
|
||||||
import it.vfsfitvnm.vimusic.R
|
import it.vfsfitvnm.vimusic.R
|
||||||
import it.vfsfitvnm.vimusic.enums.PlaylistSortBy
|
import it.vfsfitvnm.vimusic.enums.PlaylistSortBy
|
||||||
import it.vfsfitvnm.vimusic.enums.SortOrder
|
import it.vfsfitvnm.vimusic.enums.SortOrder
|
||||||
import it.vfsfitvnm.vimusic.models.DetailedSong
|
import it.vfsfitvnm.vimusic.models.Info
|
||||||
import it.vfsfitvnm.vimusic.models.Playlist
|
import it.vfsfitvnm.vimusic.models.Playlist
|
||||||
import it.vfsfitvnm.vimusic.models.Song
|
import it.vfsfitvnm.vimusic.models.Song
|
||||||
import it.vfsfitvnm.vimusic.models.SongPlaylistMap
|
import it.vfsfitvnm.vimusic.models.SongPlaylistMap
|
||||||
|
@ -69,15 +70,16 @@ import it.vfsfitvnm.vimusic.utils.formatAsDuration
|
||||||
import it.vfsfitvnm.vimusic.utils.medium
|
import it.vfsfitvnm.vimusic.utils.medium
|
||||||
import it.vfsfitvnm.vimusic.utils.semiBold
|
import it.vfsfitvnm.vimusic.utils.semiBold
|
||||||
import it.vfsfitvnm.vimusic.utils.thumbnail
|
import it.vfsfitvnm.vimusic.utils.thumbnail
|
||||||
|
import kotlin.system.measureTimeMillis
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
|
||||||
import kotlinx.coroutines.flow.flowOf
|
import kotlinx.coroutines.flow.flowOf
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
@ExperimentalAnimationApi
|
@ExperimentalAnimationApi
|
||||||
@Composable
|
@Composable
|
||||||
fun InHistoryMediaItemMenu(
|
fun InHistoryMediaItemMenu(
|
||||||
onDismiss: () -> Unit,
|
onDismiss: () -> Unit,
|
||||||
song: DetailedSong,
|
song: Song,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
val binder = LocalPlayerServiceBinder.current
|
val binder = LocalPlayerServiceBinder.current
|
||||||
|
@ -115,7 +117,7 @@ fun InPlaylistMediaItemMenu(
|
||||||
onDismiss: () -> Unit,
|
onDismiss: () -> Unit,
|
||||||
playlistId: Long,
|
playlistId: Long,
|
||||||
positionInPlaylist: Int,
|
positionInPlaylist: Int,
|
||||||
song: DetailedSong,
|
song: Song,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
NonQueuedMediaItemMenu(
|
NonQueuedMediaItemMenu(
|
||||||
|
@ -276,15 +278,43 @@ fun MediaItemMenu(
|
||||||
mutableStateOf(0.dp)
|
mutableStateOf(0.dp)
|
||||||
}
|
}
|
||||||
|
|
||||||
val likedAt by remember(mediaItem.mediaId) {
|
var albumInfo by remember {
|
||||||
Database.likedAt(mediaItem.mediaId).distinctUntilChanged()
|
mutableStateOf(mediaItem.mediaMetadata.extras?.getString("albumId")?.let { albumId ->
|
||||||
}.collectAsState(initial = null, context = Dispatchers.IO)
|
Info(albumId, null)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var artistsInfo by remember {
|
||||||
|
mutableStateOf(
|
||||||
|
mediaItem.mediaMetadata.extras?.getStringArrayList("artistNames")?.let { artistNames ->
|
||||||
|
mediaItem.mediaMetadata.extras?.getStringArrayList("artistIds")?.let { artistIds ->
|
||||||
|
artistNames.zip(artistIds).map { (authorName, authorId) ->
|
||||||
|
Info(authorId, authorName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
var likedAt by remember {
|
||||||
|
mutableStateOf<Long?>(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
if (albumInfo == null) albumInfo = Database.songAlbumInfo(mediaItem.mediaId)
|
||||||
|
if (artistsInfo == null) artistsInfo = Database.songArtistInfo(mediaItem.mediaId)
|
||||||
|
|
||||||
|
Database.likedAt(mediaItem.mediaId).collect { likedAt = it }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
AnimatedContent(
|
AnimatedContent(
|
||||||
targetState = isViewingPlaylists,
|
targetState = isViewingPlaylists,
|
||||||
transitionSpec = {
|
transitionSpec = {
|
||||||
val animationSpec = tween<IntOffset>(400)
|
val animationSpec = tween<IntOffset>(400)
|
||||||
val slideDirection = if (targetState) AnimatedContentScope.SlideDirection.Left else AnimatedContentScope.SlideDirection.Right
|
val slideDirection =
|
||||||
|
if (targetState) AnimatedContentScope.SlideDirection.Left else AnimatedContentScope.SlideDirection.Right
|
||||||
|
|
||||||
slideIntoContainer(slideDirection, animationSpec) with
|
slideIntoContainer(slideDirection, animationSpec) with
|
||||||
slideOutOfContainer(slideDirection, animationSpec)
|
slideOutOfContainer(slideDirection, animationSpec)
|
||||||
|
@ -371,7 +401,8 @@ fun MediaItemMenu(
|
||||||
.padding(end = 12.dp)
|
.padding(end = 12.dp)
|
||||||
) {
|
) {
|
||||||
SongItem(
|
SongItem(
|
||||||
thumbnailUrl = mediaItem.mediaMetadata.artworkUri.thumbnail(thumbnailSizePx)?.toString(),
|
thumbnailUrl = mediaItem.mediaMetadata.artworkUri.thumbnail(thumbnailSizePx)
|
||||||
|
?.toString(),
|
||||||
title = mediaItem.mediaMetadata.title.toString(),
|
title = mediaItem.mediaMetadata.title.toString(),
|
||||||
authors = mediaItem.mediaMetadata.artist.toString(),
|
authors = mediaItem.mediaMetadata.artist.toString(),
|
||||||
duration = null,
|
duration = null,
|
||||||
|
@ -601,7 +632,10 @@ fun MediaItemMenu(
|
||||||
text = "${formatAsDuration(it)} left",
|
text = "${formatAsDuration(it)} left",
|
||||||
style = typography.xxs.medium,
|
style = typography.xxs.medium,
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.background(color = colorPalette.background0, shape = RoundedCornerShape(16.dp))
|
.background(
|
||||||
|
color = colorPalette.background0,
|
||||||
|
shape = RoundedCornerShape(16.dp)
|
||||||
|
)
|
||||||
.padding(horizontal = 16.dp, vertical = 8.dp)
|
.padding(horizontal = 16.dp, vertical = 8.dp)
|
||||||
.animateContentSize()
|
.animateContentSize()
|
||||||
)
|
)
|
||||||
|
@ -619,7 +653,9 @@ fun MediaItemMenu(
|
||||||
Image(
|
Image(
|
||||||
painter = painterResource(R.drawable.chevron_forward),
|
painter = painterResource(R.drawable.chevron_forward),
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
colorFilter = androidx.compose.ui.graphics.ColorFilter.tint(colorPalette.textSecondary),
|
colorFilter = androidx.compose.ui.graphics.ColorFilter.tint(
|
||||||
|
colorPalette.textSecondary
|
||||||
|
),
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.size(16.dp)
|
.size(16.dp)
|
||||||
)
|
)
|
||||||
|
@ -628,7 +664,7 @@ fun MediaItemMenu(
|
||||||
}
|
}
|
||||||
|
|
||||||
onGoToAlbum?.let { onGoToAlbum ->
|
onGoToAlbum?.let { onGoToAlbum ->
|
||||||
mediaItem.mediaMetadata.extras?.getString("albumId")?.let { albumId ->
|
albumInfo?.let { (albumId) ->
|
||||||
MenuEntry(
|
MenuEntry(
|
||||||
icon = R.drawable.disc,
|
icon = R.drawable.disc,
|
||||||
text = "Go to album",
|
text = "Go to album",
|
||||||
|
@ -641,13 +677,7 @@ fun MediaItemMenu(
|
||||||
}
|
}
|
||||||
|
|
||||||
onGoToArtist?.let { onGoToArtist ->
|
onGoToArtist?.let { onGoToArtist ->
|
||||||
mediaItem.mediaMetadata.extras?.getStringArrayList("artistNames")
|
artistsInfo?.forEach { (authorId, authorName) ->
|
||||||
?.let { artistNames ->
|
|
||||||
mediaItem.mediaMetadata.extras?.getStringArrayList("artistIds")
|
|
||||||
?.let { artistIds ->
|
|
||||||
artistNames.zip(artistIds)
|
|
||||||
.forEach { (authorName, authorId) ->
|
|
||||||
if (authorId != null) {
|
|
||||||
MenuEntry(
|
MenuEntry(
|
||||||
icon = R.drawable.person,
|
icon = R.drawable.person,
|
||||||
text = "More of $authorName",
|
text = "More of $authorName",
|
||||||
|
@ -658,9 +688,6 @@ fun MediaItemMenu(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onRemoveFromQueue?.let { onRemoveFromQueue ->
|
onRemoveFromQueue?.let { onRemoveFromQueue ->
|
||||||
MenuEntry(
|
MenuEntry(
|
||||||
|
|
|
@ -19,7 +19,6 @@ import androidx.compose.ui.unit.Dp
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.media3.common.MediaItem
|
import androidx.media3.common.MediaItem
|
||||||
import coil.compose.AsyncImage
|
import coil.compose.AsyncImage
|
||||||
import it.vfsfitvnm.vimusic.models.DetailedSong
|
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.TextPlaceholder
|
import it.vfsfitvnm.vimusic.ui.components.themed.TextPlaceholder
|
||||||
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
|
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
|
||||||
import it.vfsfitvnm.vimusic.ui.styling.shimmer
|
import it.vfsfitvnm.vimusic.ui.styling.shimmer
|
||||||
|
@ -28,6 +27,7 @@ import it.vfsfitvnm.vimusic.utils.secondary
|
||||||
import it.vfsfitvnm.vimusic.utils.semiBold
|
import it.vfsfitvnm.vimusic.utils.semiBold
|
||||||
import it.vfsfitvnm.vimusic.utils.thumbnail
|
import it.vfsfitvnm.vimusic.utils.thumbnail
|
||||||
import it.vfsfitvnm.innertube.Innertube
|
import it.vfsfitvnm.innertube.Innertube
|
||||||
|
import it.vfsfitvnm.vimusic.models.Song
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SongItem(
|
fun SongItem(
|
||||||
|
@ -69,7 +69,7 @@ fun SongItem(
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SongItem(
|
fun SongItem(
|
||||||
song: DetailedSong,
|
song: Song,
|
||||||
thumbnailSizePx: Int,
|
thumbnailSizePx: Int,
|
||||||
thumbnailSizeDp: Dp,
|
thumbnailSizeDp: Dp,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
|
|
|
@ -27,7 +27,7 @@ import it.vfsfitvnm.vimusic.Database
|
||||||
import it.vfsfitvnm.vimusic.LocalPlayerAwareWindowInsets
|
import it.vfsfitvnm.vimusic.LocalPlayerAwareWindowInsets
|
||||||
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
|
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
|
||||||
import it.vfsfitvnm.vimusic.R
|
import it.vfsfitvnm.vimusic.R
|
||||||
import it.vfsfitvnm.vimusic.models.DetailedSong
|
import it.vfsfitvnm.vimusic.models.Song
|
||||||
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
|
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
|
||||||
import it.vfsfitvnm.vimusic.ui.components.ShimmerHost
|
import it.vfsfitvnm.vimusic.ui.components.ShimmerHost
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.FloatingActionsContainerWithScrollToTop
|
import it.vfsfitvnm.vimusic.ui.components.themed.FloatingActionsContainerWithScrollToTop
|
||||||
|
@ -59,7 +59,7 @@ fun AlbumSongs(
|
||||||
val binder = LocalPlayerServiceBinder.current
|
val binder = LocalPlayerServiceBinder.current
|
||||||
val menuState = LocalMenuState.current
|
val menuState = LocalMenuState.current
|
||||||
|
|
||||||
var songs by persistList<DetailedSong>("album/$browseId/songs")
|
var songs by persistList<Song>("album/$browseId/songs")
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
Database.albumSongs(browseId).collect { songs = it }
|
Database.albumSongs(browseId).collect { songs = it }
|
||||||
|
@ -89,7 +89,7 @@ fun AlbumSongs(
|
||||||
text = "Enqueue",
|
text = "Enqueue",
|
||||||
enabled = songs.isNotEmpty(),
|
enabled = songs.isNotEmpty(),
|
||||||
onClick = {
|
onClick = {
|
||||||
binder?.player?.enqueue(songs.map(DetailedSong::asMediaItem))
|
binder?.player?.enqueue(songs.map(Song::asMediaItem))
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -133,7 +133,7 @@ fun AlbumSongs(
|
||||||
onClick = {
|
onClick = {
|
||||||
binder?.stopRadio()
|
binder?.stopRadio()
|
||||||
binder?.player?.forcePlayAtIndex(
|
binder?.player?.forcePlayAtIndex(
|
||||||
songs.map(DetailedSong::asMediaItem),
|
songs.map(Song::asMediaItem),
|
||||||
index
|
index
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -162,7 +162,7 @@ fun AlbumSongs(
|
||||||
if (songs.isNotEmpty()) {
|
if (songs.isNotEmpty()) {
|
||||||
binder?.stopRadio()
|
binder?.stopRadio()
|
||||||
binder?.player?.forcePlayFromBeginning(
|
binder?.player?.forcePlayFromBeginning(
|
||||||
songs.shuffled().map(DetailedSong::asMediaItem)
|
songs.shuffled().map(Song::asMediaItem)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ import it.vfsfitvnm.vimusic.Database
|
||||||
import it.vfsfitvnm.vimusic.LocalPlayerAwareWindowInsets
|
import it.vfsfitvnm.vimusic.LocalPlayerAwareWindowInsets
|
||||||
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
|
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
|
||||||
import it.vfsfitvnm.vimusic.R
|
import it.vfsfitvnm.vimusic.R
|
||||||
import it.vfsfitvnm.vimusic.models.DetailedSong
|
import it.vfsfitvnm.vimusic.models.Song
|
||||||
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
|
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
|
||||||
import it.vfsfitvnm.vimusic.ui.components.ShimmerHost
|
import it.vfsfitvnm.vimusic.ui.components.ShimmerHost
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.FloatingActionsContainerWithScrollToTop
|
import it.vfsfitvnm.vimusic.ui.components.themed.FloatingActionsContainerWithScrollToTop
|
||||||
|
@ -53,7 +53,7 @@ fun ArtistLocalSongs(
|
||||||
val (colorPalette) = LocalAppearance.current
|
val (colorPalette) = LocalAppearance.current
|
||||||
val menuState = LocalMenuState.current
|
val menuState = LocalMenuState.current
|
||||||
|
|
||||||
var songs by persist<List<DetailedSong>?>("artist/$browseId/localSongs")
|
var songs by persist<List<Song>?>("artist/$browseId/localSongs")
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
Database.artistSongs(browseId).collect { songs = it }
|
Database.artistSongs(browseId).collect { songs = it }
|
||||||
|
@ -84,7 +84,7 @@ fun ArtistLocalSongs(
|
||||||
text = "Enqueue",
|
text = "Enqueue",
|
||||||
enabled = !songs.isNullOrEmpty(),
|
enabled = !songs.isNullOrEmpty(),
|
||||||
onClick = {
|
onClick = {
|
||||||
binder?.player?.enqueue(songs!!.map(DetailedSong::asMediaItem))
|
binder?.player?.enqueue(songs!!.map(Song::asMediaItem))
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -115,7 +115,7 @@ fun ArtistLocalSongs(
|
||||||
onClick = {
|
onClick = {
|
||||||
binder?.stopRadio()
|
binder?.stopRadio()
|
||||||
binder?.player?.forcePlayAtIndex(
|
binder?.player?.forcePlayAtIndex(
|
||||||
songs.map(DetailedSong::asMediaItem),
|
songs.map(Song::asMediaItem),
|
||||||
index
|
index
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -139,7 +139,7 @@ fun ArtistLocalSongs(
|
||||||
if (songs.isNotEmpty()) {
|
if (songs.isNotEmpty()) {
|
||||||
binder?.stopRadio()
|
binder?.stopRadio()
|
||||||
binder?.player?.forcePlayFromBeginning(
|
binder?.player?.forcePlayFromBeginning(
|
||||||
songs.shuffled().map(DetailedSong::asMediaItem)
|
songs.shuffled().map(Song::asMediaItem)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,8 @@ import it.vfsfitvnm.vimusic.LocalPlayerAwareWindowInsets
|
||||||
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
|
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
|
||||||
import it.vfsfitvnm.vimusic.R
|
import it.vfsfitvnm.vimusic.R
|
||||||
import it.vfsfitvnm.vimusic.enums.BuiltInPlaylist
|
import it.vfsfitvnm.vimusic.enums.BuiltInPlaylist
|
||||||
import it.vfsfitvnm.vimusic.models.DetailedSong
|
import it.vfsfitvnm.vimusic.models.Song
|
||||||
|
import it.vfsfitvnm.vimusic.models.SongWithContentLength
|
||||||
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
|
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.FloatingActionsContainerWithScrollToTop
|
import it.vfsfitvnm.vimusic.ui.components.themed.FloatingActionsContainerWithScrollToTop
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.Header
|
import it.vfsfitvnm.vimusic.ui.components.themed.Header
|
||||||
|
@ -53,7 +54,7 @@ fun BuiltInPlaylistSongs(builtInPlaylist: BuiltInPlaylist) {
|
||||||
val binder = LocalPlayerServiceBinder.current
|
val binder = LocalPlayerServiceBinder.current
|
||||||
val menuState = LocalMenuState.current
|
val menuState = LocalMenuState.current
|
||||||
|
|
||||||
var songs by persistList<DetailedSong>("${builtInPlaylist.name}/songs")
|
var songs by persistList<Song>("${builtInPlaylist.name}/songs")
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
when (builtInPlaylist) {
|
when (builtInPlaylist) {
|
||||||
|
@ -66,9 +67,9 @@ fun BuiltInPlaylistSongs(builtInPlaylist: BuiltInPlaylist) {
|
||||||
.map { songs ->
|
.map { songs ->
|
||||||
songs.filter { song ->
|
songs.filter { song ->
|
||||||
song.contentLength?.let {
|
song.contentLength?.let {
|
||||||
binder?.cache?.isCached(song.id, 0, song.contentLength)
|
binder?.cache?.isCached(song.song.id, 0, song.contentLength)
|
||||||
} ?: false
|
} ?: false
|
||||||
}
|
}.map(SongWithContentLength::song)
|
||||||
}
|
}
|
||||||
}.collect { songs = it }
|
}.collect { songs = it }
|
||||||
}
|
}
|
||||||
|
@ -103,7 +104,7 @@ fun BuiltInPlaylistSongs(builtInPlaylist: BuiltInPlaylist) {
|
||||||
text = "Enqueue",
|
text = "Enqueue",
|
||||||
enabled = songs.isNotEmpty(),
|
enabled = songs.isNotEmpty(),
|
||||||
onClick = {
|
onClick = {
|
||||||
binder?.player?.enqueue(songs.map(DetailedSong::asMediaItem))
|
binder?.player?.enqueue(songs.map(Song::asMediaItem))
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -143,7 +144,7 @@ fun BuiltInPlaylistSongs(builtInPlaylist: BuiltInPlaylist) {
|
||||||
onClick = {
|
onClick = {
|
||||||
binder?.stopRadio()
|
binder?.stopRadio()
|
||||||
binder?.player?.forcePlayAtIndex(
|
binder?.player?.forcePlayAtIndex(
|
||||||
songs.map(DetailedSong::asMediaItem),
|
songs.map(Song::asMediaItem),
|
||||||
index
|
index
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -160,7 +161,7 @@ fun BuiltInPlaylistSongs(builtInPlaylist: BuiltInPlaylist) {
|
||||||
if (songs.isNotEmpty()) {
|
if (songs.isNotEmpty()) {
|
||||||
binder?.stopRadio()
|
binder?.stopRadio()
|
||||||
binder?.player?.forcePlayFromBeginning(
|
binder?.player?.forcePlayFromBeginning(
|
||||||
songs.shuffled().map(DetailedSong::asMediaItem)
|
songs.shuffled().map(Song::asMediaItem)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,8 @@ import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
|
||||||
import it.vfsfitvnm.vimusic.R
|
import it.vfsfitvnm.vimusic.R
|
||||||
import it.vfsfitvnm.vimusic.enums.SongSortBy
|
import it.vfsfitvnm.vimusic.enums.SongSortBy
|
||||||
import it.vfsfitvnm.vimusic.enums.SortOrder
|
import it.vfsfitvnm.vimusic.enums.SortOrder
|
||||||
import it.vfsfitvnm.vimusic.models.DetailedSong
|
import it.vfsfitvnm.vimusic.models.Song
|
||||||
|
import it.vfsfitvnm.vimusic.query
|
||||||
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
|
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.FloatingActionsContainerWithScrollToTop
|
import it.vfsfitvnm.vimusic.ui.components.themed.FloatingActionsContainerWithScrollToTop
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.Header
|
import it.vfsfitvnm.vimusic.ui.components.themed.Header
|
||||||
|
@ -58,6 +59,8 @@ import it.vfsfitvnm.vimusic.utils.rememberPreference
|
||||||
import it.vfsfitvnm.vimusic.utils.semiBold
|
import it.vfsfitvnm.vimusic.utils.semiBold
|
||||||
import it.vfsfitvnm.vimusic.utils.songSortByKey
|
import it.vfsfitvnm.vimusic.utils.songSortByKey
|
||||||
import it.vfsfitvnm.vimusic.utils.songSortOrderKey
|
import it.vfsfitvnm.vimusic.utils.songSortOrderKey
|
||||||
|
import kotlin.system.measureTimeMillis
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
|
|
||||||
@ExperimentalFoundationApi
|
@ExperimentalFoundationApi
|
||||||
@ExperimentalAnimationApi
|
@ExperimentalAnimationApi
|
||||||
|
@ -75,9 +78,12 @@ fun HomeSongs(
|
||||||
var sortBy by rememberPreference(songSortByKey, SongSortBy.DateAdded)
|
var sortBy by rememberPreference(songSortByKey, SongSortBy.DateAdded)
|
||||||
var sortOrder by rememberPreference(songSortOrderKey, SortOrder.Descending)
|
var sortOrder by rememberPreference(songSortOrderKey, SortOrder.Descending)
|
||||||
|
|
||||||
var items by persistList<DetailedSong>("home/songs")
|
var items by persistList<Song>("home/songs")
|
||||||
|
|
||||||
LaunchedEffect(sortBy, sortOrder) {
|
LaunchedEffect(sortBy, sortOrder) {
|
||||||
|
// 670, 58, 97, 91, 94
|
||||||
|
println("took ${measureTimeMillis { Database.songs(sortBy, sortOrder).first() }}ms")
|
||||||
|
|
||||||
Database.songs(sortBy, sortOrder).collect { items = it }
|
Database.songs(sortBy, sortOrder).collect { items = it }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,7 +181,7 @@ fun HomeSongs(
|
||||||
onClick = {
|
onClick = {
|
||||||
binder?.stopRadio()
|
binder?.stopRadio()
|
||||||
binder?.player?.forcePlayAtIndex(
|
binder?.player?.forcePlayAtIndex(
|
||||||
items.map(DetailedSong::asMediaItem),
|
items.map(Song::asMediaItem),
|
||||||
index
|
index
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,7 +47,7 @@ import it.vfsfitvnm.vimusic.Database
|
||||||
import it.vfsfitvnm.vimusic.LocalPlayerAwareWindowInsets
|
import it.vfsfitvnm.vimusic.LocalPlayerAwareWindowInsets
|
||||||
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
|
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
|
||||||
import it.vfsfitvnm.vimusic.R
|
import it.vfsfitvnm.vimusic.R
|
||||||
import it.vfsfitvnm.vimusic.models.DetailedSong
|
import it.vfsfitvnm.vimusic.models.Song
|
||||||
import it.vfsfitvnm.vimusic.query
|
import it.vfsfitvnm.vimusic.query
|
||||||
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
|
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
|
||||||
import it.vfsfitvnm.vimusic.ui.components.ShimmerHost
|
import it.vfsfitvnm.vimusic.ui.components.ShimmerHost
|
||||||
|
@ -89,7 +89,7 @@ fun QuickPicks(
|
||||||
val menuState = LocalMenuState.current
|
val menuState = LocalMenuState.current
|
||||||
val windowInsets = LocalPlayerAwareWindowInsets.current
|
val windowInsets = LocalPlayerAwareWindowInsets.current
|
||||||
|
|
||||||
var trending by persist<DetailedSong?>("home/trending")
|
var trending by persist<Song?>("home/trending")
|
||||||
|
|
||||||
var relatedPageResult by persist<Result<Innertube.RelatedPage?>?>(tag = "home/relatedPageResult")
|
var relatedPageResult by persist<Result<Innertube.RelatedPage?>?>(tag = "home/relatedPageResult")
|
||||||
|
|
||||||
|
|
|
@ -36,8 +36,8 @@ import it.vfsfitvnm.vimusic.Database
|
||||||
import it.vfsfitvnm.vimusic.LocalPlayerAwareWindowInsets
|
import it.vfsfitvnm.vimusic.LocalPlayerAwareWindowInsets
|
||||||
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
|
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
|
||||||
import it.vfsfitvnm.vimusic.R
|
import it.vfsfitvnm.vimusic.R
|
||||||
import it.vfsfitvnm.vimusic.models.DetailedSong
|
|
||||||
import it.vfsfitvnm.vimusic.models.PlaylistWithSongs
|
import it.vfsfitvnm.vimusic.models.PlaylistWithSongs
|
||||||
|
import it.vfsfitvnm.vimusic.models.Song
|
||||||
import it.vfsfitvnm.vimusic.models.SongPlaylistMap
|
import it.vfsfitvnm.vimusic.models.SongPlaylistMap
|
||||||
import it.vfsfitvnm.vimusic.query
|
import it.vfsfitvnm.vimusic.query
|
||||||
import it.vfsfitvnm.vimusic.transaction
|
import it.vfsfitvnm.vimusic.transaction
|
||||||
|
@ -158,7 +158,7 @@ fun LocalPlaylistSongs(
|
||||||
enabled = playlistWithSongs?.songs?.isNotEmpty() == true,
|
enabled = playlistWithSongs?.songs?.isNotEmpty() == true,
|
||||||
onClick = {
|
onClick = {
|
||||||
playlistWithSongs?.songs
|
playlistWithSongs?.songs
|
||||||
?.map(DetailedSong::asMediaItem)
|
?.map(Song::asMediaItem)
|
||||||
?.let { mediaItems ->
|
?.let { mediaItems ->
|
||||||
binder?.player?.enqueue(mediaItems)
|
binder?.player?.enqueue(mediaItems)
|
||||||
}
|
}
|
||||||
|
@ -266,7 +266,7 @@ fun LocalPlaylistSongs(
|
||||||
},
|
},
|
||||||
onClick = {
|
onClick = {
|
||||||
playlistWithSongs?.songs
|
playlistWithSongs?.songs
|
||||||
?.map(DetailedSong::asMediaItem)
|
?.map(Song::asMediaItem)
|
||||||
?.let { mediaItems ->
|
?.let { mediaItems ->
|
||||||
binder?.stopRadio()
|
binder?.stopRadio()
|
||||||
binder?.player?.forcePlayAtIndex(mediaItems, index)
|
binder?.player?.forcePlayAtIndex(mediaItems, index)
|
||||||
|
@ -288,7 +288,7 @@ fun LocalPlaylistSongs(
|
||||||
if (songs.isNotEmpty()) {
|
if (songs.isNotEmpty()) {
|
||||||
binder?.stopRadio()
|
binder?.stopRadio()
|
||||||
binder?.player?.forcePlayFromBeginning(
|
binder?.player?.forcePlayFromBeginning(
|
||||||
songs.shuffled().map(DetailedSong::asMediaItem)
|
songs.shuffled().map(Song::asMediaItem)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ import it.vfsfitvnm.innertube.models.NavigationEndpoint
|
||||||
import it.vfsfitvnm.vimusic.Database
|
import it.vfsfitvnm.vimusic.Database
|
||||||
import it.vfsfitvnm.vimusic.LocalPlayerAwareWindowInsets
|
import it.vfsfitvnm.vimusic.LocalPlayerAwareWindowInsets
|
||||||
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
|
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
|
||||||
import it.vfsfitvnm.vimusic.models.DetailedSong
|
import it.vfsfitvnm.vimusic.models.Song
|
||||||
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
|
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.FloatingActionsContainerWithScrollToTop
|
import it.vfsfitvnm.vimusic.ui.components.themed.FloatingActionsContainerWithScrollToTop
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.Header
|
import it.vfsfitvnm.vimusic.ui.components.themed.Header
|
||||||
|
@ -54,7 +54,7 @@ fun LocalSongSearch(
|
||||||
val binder = LocalPlayerServiceBinder.current
|
val binder = LocalPlayerServiceBinder.current
|
||||||
val menuState = LocalMenuState.current
|
val menuState = LocalMenuState.current
|
||||||
|
|
||||||
var items by persistList<DetailedSong>("search/local/songs")
|
var items by persistList<Song>("search/local/songs")
|
||||||
|
|
||||||
LaunchedEffect(textFieldValue.text) {
|
LaunchedEffect(textFieldValue.text) {
|
||||||
if (textFieldValue.text.length > 1) {
|
if (textFieldValue.text.length > 1) {
|
||||||
|
@ -105,7 +105,7 @@ fun LocalSongSearch(
|
||||||
|
|
||||||
items(
|
items(
|
||||||
items = items,
|
items = items,
|
||||||
key = DetailedSong::id,
|
key = Song::id,
|
||||||
) { song ->
|
) { song ->
|
||||||
SongItem(
|
SongItem(
|
||||||
song = song,
|
song = song,
|
||||||
|
|
|
@ -7,11 +7,11 @@ import androidx.core.net.toUri
|
||||||
import androidx.core.os.bundleOf
|
import androidx.core.os.bundleOf
|
||||||
import androidx.media3.common.MediaItem
|
import androidx.media3.common.MediaItem
|
||||||
import androidx.media3.common.MediaMetadata
|
import androidx.media3.common.MediaMetadata
|
||||||
import it.vfsfitvnm.vimusic.models.DetailedSong
|
|
||||||
import it.vfsfitvnm.innertube.Innertube
|
import it.vfsfitvnm.innertube.Innertube
|
||||||
import it.vfsfitvnm.innertube.models.bodies.ContinuationBody
|
import it.vfsfitvnm.innertube.models.bodies.ContinuationBody
|
||||||
import it.vfsfitvnm.innertube.requests.playlistPage
|
import it.vfsfitvnm.innertube.requests.playlistPage
|
||||||
import it.vfsfitvnm.innertube.utils.plus
|
import it.vfsfitvnm.innertube.utils.plus
|
||||||
|
import it.vfsfitvnm.vimusic.models.Song
|
||||||
|
|
||||||
val Innertube.SongItem.asMediaItem: MediaItem
|
val Innertube.SongItem.asMediaItem: MediaItem
|
||||||
get() = MediaItem.Builder()
|
get() = MediaItem.Builder()
|
||||||
|
@ -26,7 +26,6 @@ val Innertube.SongItem.asMediaItem: MediaItem
|
||||||
.setArtworkUri(thumbnail?.url?.toUri())
|
.setArtworkUri(thumbnail?.url?.toUri())
|
||||||
.setExtras(
|
.setExtras(
|
||||||
bundleOf(
|
bundleOf(
|
||||||
"videoId" to key,
|
|
||||||
"albumId" to album?.endpoint?.browseId,
|
"albumId" to album?.endpoint?.browseId,
|
||||||
"durationText" to durationText,
|
"durationText" to durationText,
|
||||||
"artistNames" to authors?.filter { it.endpoint != null }?.mapNotNull { it.name },
|
"artistNames" to authors?.filter { it.endpoint != null }?.mapNotNull { it.name },
|
||||||
|
@ -49,7 +48,6 @@ val Innertube.VideoItem.asMediaItem: MediaItem
|
||||||
.setArtworkUri(thumbnail?.url?.toUri())
|
.setArtworkUri(thumbnail?.url?.toUri())
|
||||||
.setExtras(
|
.setExtras(
|
||||||
bundleOf(
|
bundleOf(
|
||||||
"videoId" to key,
|
|
||||||
"durationText" to durationText,
|
"durationText" to durationText,
|
||||||
"artistNames" to if (isOfficialMusicVideo) authors?.filter { it.endpoint != null }?.mapNotNull { it.name } else null,
|
"artistNames" to if (isOfficialMusicVideo) authors?.filter { it.endpoint != null }?.mapNotNull { it.name } else null,
|
||||||
"artistIds" to if (isOfficialMusicVideo) authors?.mapNotNull { it.endpoint?.browseId } else null,
|
"artistIds" to if (isOfficialMusicVideo) authors?.mapNotNull { it.endpoint?.browseId } else null,
|
||||||
|
@ -59,7 +57,7 @@ val Innertube.VideoItem.asMediaItem: MediaItem
|
||||||
)
|
)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
val DetailedSong.asMediaItem: MediaItem
|
val Song.asMediaItem: MediaItem
|
||||||
get() = MediaItem.Builder()
|
get() = MediaItem.Builder()
|
||||||
.setMediaMetadata(
|
.setMediaMetadata(
|
||||||
MediaMetadata.Builder()
|
MediaMetadata.Builder()
|
||||||
|
@ -68,10 +66,6 @@ val DetailedSong.asMediaItem: MediaItem
|
||||||
.setArtworkUri(thumbnailUrl?.toUri())
|
.setArtworkUri(thumbnailUrl?.toUri())
|
||||||
.setExtras(
|
.setExtras(
|
||||||
bundleOf(
|
bundleOf(
|
||||||
"videoId" to id,
|
|
||||||
"albumId" to albumId,
|
|
||||||
"artistNames" to artists?.map { it.name },
|
|
||||||
"artistIds" to artists?.map { it.id },
|
|
||||||
"durationText" to durationText
|
"durationText" to durationText
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in a new issue