address review: play songs by id, reverse album sort, tab-scoped scroll reset, literal search and stable tiebreakers

This commit is contained in:
austinried
2026-10-04 00:29:55 +00:00
parent e99ed1dc45
commit f1c030f0e1
8 changed files with 123 additions and 72 deletions
@@ -92,8 +92,8 @@ interface LibraryDao {
@Query(
"SELECT * FROM albums WHERE sourceId = :sourceId " +
"AND (:starredFilter = 0 OR (:starredFilter = 1 AND starred IS NOT NULL) OR (:starredFilter = 2 AND starred IS NULL)) " +
"AND (:search = '' OR name LIKE '%' || :search || '%' OR albumArtist LIKE '%' || :search || '%') " +
"ORDER BY name COLLATE NOCASE",
"AND (:search = '' OR instr(lower(name), lower(:search)) > 0 OR instr(lower(albumArtist), lower(:search)) > 0) " +
"ORDER BY name COLLATE NOCASE, id",
)
fun albumsByName(
sourceId: Long,
@@ -104,8 +104,8 @@ interface LibraryDao {
@Query(
"SELECT * FROM albums WHERE sourceId = :sourceId " +
"AND (:starredFilter = 0 OR (:starredFilter = 1 AND starred IS NOT NULL) OR (:starredFilter = 2 AND starred IS NULL)) " +
"AND (:search = '' OR name LIKE '%' || :search || '%' OR albumArtist LIKE '%' || :search || '%') " +
"ORDER BY name COLLATE NOCASE DESC",
"AND (:search = '' OR instr(lower(name), lower(:search)) > 0 OR instr(lower(albumArtist), lower(:search)) > 0) " +
"ORDER BY name COLLATE NOCASE DESC, id",
)
fun albumsByNameReversed(
sourceId: Long,
@@ -116,8 +116,8 @@ interface LibraryDao {
@Query(
"SELECT * FROM albums WHERE sourceId = :sourceId " +
"AND (:starredFilter = 0 OR (:starredFilter = 1 AND starred IS NOT NULL) OR (:starredFilter = 2 AND starred IS NULL)) " +
"AND (:search = '' OR name LIKE '%' || :search || '%' OR albumArtist LIKE '%' || :search || '%') " +
"ORDER BY albumArtist COLLATE NOCASE, year, name COLLATE NOCASE",
"AND (:search = '' OR instr(lower(name), lower(:search)) > 0 OR instr(lower(albumArtist), lower(:search)) > 0) " +
"ORDER BY albumArtist COLLATE NOCASE, year, name COLLATE NOCASE, id",
)
fun albumsByArtist(
sourceId: Long,
@@ -128,8 +128,8 @@ interface LibraryDao {
@Query(
"SELECT * FROM albums WHERE sourceId = :sourceId " +
"AND (:starredFilter = 0 OR (:starredFilter = 1 AND starred IS NOT NULL) OR (:starredFilter = 2 AND starred IS NULL)) " +
"AND (:search = '' OR name LIKE '%' || :search || '%' OR albumArtist LIKE '%' || :search || '%') " +
"ORDER BY albumArtist COLLATE NOCASE DESC, year DESC, name COLLATE NOCASE DESC",
"AND (:search = '' OR instr(lower(name), lower(:search)) > 0 OR instr(lower(albumArtist), lower(:search)) > 0) " +
"ORDER BY albumArtist COLLATE NOCASE DESC, year DESC, name COLLATE NOCASE DESC, id",
)
fun albumsByArtistReversed(
sourceId: Long,
@@ -140,8 +140,8 @@ interface LibraryDao {
@Query(
"SELECT * FROM albums WHERE sourceId = :sourceId " +
"AND (:starredFilter = 0 OR (:starredFilter = 1 AND starred IS NOT NULL) OR (:starredFilter = 2 AND starred IS NULL)) " +
"AND (:search = '' OR name LIKE '%' || :search || '%' OR albumArtist LIKE '%' || :search || '%') " +
"ORDER BY year DESC, name COLLATE NOCASE",
"AND (:search = '' OR instr(lower(name), lower(:search)) > 0 OR instr(lower(albumArtist), lower(:search)) > 0) " +
"ORDER BY year DESC, name COLLATE NOCASE, id",
)
fun albumsByYear(
sourceId: Long,
@@ -152,8 +152,8 @@ interface LibraryDao {
@Query(
"SELECT * FROM albums WHERE sourceId = :sourceId " +
"AND (:starredFilter = 0 OR (:starredFilter = 1 AND starred IS NOT NULL) OR (:starredFilter = 2 AND starred IS NULL)) " +
"AND (:search = '' OR name LIKE '%' || :search || '%' OR albumArtist LIKE '%' || :search || '%') " +
"ORDER BY year ASC, name COLLATE NOCASE",
"AND (:search = '' OR instr(lower(name), lower(:search)) > 0 OR instr(lower(albumArtist), lower(:search)) > 0) " +
"ORDER BY year ASC, name COLLATE NOCASE, id",
)
fun albumsByYearReversed(
sourceId: Long,
@@ -164,8 +164,8 @@ interface LibraryDao {
@Query(
"SELECT * FROM albums WHERE sourceId = :sourceId " +
"AND (:starredFilter = 0 OR (:starredFilter = 1 AND starred IS NOT NULL) OR (:starredFilter = 2 AND starred IS NULL)) " +
"AND (:search = '' OR name LIKE '%' || :search || '%' OR albumArtist LIKE '%' || :search || '%') " +
"ORDER BY created DESC, name COLLATE NOCASE",
"AND (:search = '' OR instr(lower(name), lower(:search)) > 0 OR instr(lower(albumArtist), lower(:search)) > 0) " +
"ORDER BY created DESC, name COLLATE NOCASE, id",
)
fun albumsByRecentlyAdded(
sourceId: Long,
@@ -176,8 +176,8 @@ interface LibraryDao {
@Query(
"SELECT * FROM albums WHERE sourceId = :sourceId " +
"AND (:starredFilter = 0 OR (:starredFilter = 1 AND starred IS NOT NULL) OR (:starredFilter = 2 AND starred IS NULL)) " +
"AND (:search = '' OR name LIKE '%' || :search || '%' OR albumArtist LIKE '%' || :search || '%') " +
"ORDER BY created ASC, name COLLATE NOCASE",
"AND (:search = '' OR instr(lower(name), lower(:search)) > 0 OR instr(lower(albumArtist), lower(:search)) > 0) " +
"ORDER BY created ASC, name COLLATE NOCASE, id",
)
fun albumsByRecentlyAddedReversed(
sourceId: Long,
@@ -188,8 +188,8 @@ interface LibraryDao {
@Query(
"SELECT * FROM albums WHERE sourceId = :sourceId " +
"AND (:starredFilter = 0 OR (:starredFilter = 1 AND starred IS NOT NULL) OR (:starredFilter = 2 AND starred IS NULL)) " +
"AND (:search = '' OR name LIKE '%' || :search || '%' OR albumArtist LIKE '%' || :search || '%') " +
"ORDER BY starred IS NULL, starred DESC, name COLLATE NOCASE",
"AND (:search = '' OR instr(lower(name), lower(:search)) > 0 OR instr(lower(albumArtist), lower(:search)) > 0) " +
"ORDER BY starred IS NULL, starred DESC, name COLLATE NOCASE, id",
)
fun albumsByStarred(
sourceId: Long,
@@ -200,8 +200,8 @@ interface LibraryDao {
@Query(
"SELECT * FROM albums WHERE sourceId = :sourceId " +
"AND (:starredFilter = 0 OR (:starredFilter = 1 AND starred IS NOT NULL) OR (:starredFilter = 2 AND starred IS NULL)) " +
"AND (:search = '' OR name LIKE '%' || :search || '%' OR albumArtist LIKE '%' || :search || '%') " +
"ORDER BY starred IS NULL, starred ASC, name COLLATE NOCASE",
"AND (:search = '' OR instr(lower(name), lower(:search)) > 0 OR instr(lower(albumArtist), lower(:search)) > 0) " +
"ORDER BY starred IS NULL, starred ASC, name COLLATE NOCASE, id",
)
fun albumsByStarredReversed(
sourceId: Long,
@@ -212,8 +212,8 @@ interface LibraryDao {
@Query(
"SELECT * FROM artists WHERE sourceId = :sourceId " +
"AND (:starredFilter = 0 OR (:starredFilter = 1 AND starred IS NOT NULL) OR (:starredFilter = 2 AND starred IS NULL)) " +
"AND (:search = '' OR name LIKE '%' || :search || '%') " +
"ORDER BY name COLLATE NOCASE",
"AND (:search = '' OR instr(lower(name), lower(:search)) > 0) " +
"ORDER BY name COLLATE NOCASE, id",
)
fun artistsByName(
sourceId: Long,
@@ -224,8 +224,8 @@ interface LibraryDao {
@Query(
"SELECT * FROM artists WHERE sourceId = :sourceId " +
"AND (:starredFilter = 0 OR (:starredFilter = 1 AND starred IS NOT NULL) OR (:starredFilter = 2 AND starred IS NULL)) " +
"AND (:search = '' OR name LIKE '%' || :search || '%') " +
"ORDER BY name COLLATE NOCASE DESC",
"AND (:search = '' OR instr(lower(name), lower(:search)) > 0) " +
"ORDER BY name COLLATE NOCASE DESC, id",
)
fun artistsByNameReversed(
sourceId: Long,
@@ -236,8 +236,8 @@ interface LibraryDao {
@Query(
"SELECT * FROM artists WHERE sourceId = :sourceId " +
"AND (:starredFilter = 0 OR (:starredFilter = 1 AND starred IS NOT NULL) OR (:starredFilter = 2 AND starred IS NULL)) " +
"AND (:search = '' OR name LIKE '%' || :search || '%') " +
"ORDER BY albumCount DESC, name COLLATE NOCASE",
"AND (:search = '' OR instr(lower(name), lower(:search)) > 0) " +
"ORDER BY albumCount DESC, name COLLATE NOCASE, id",
)
fun artistsByAlbumCount(
sourceId: Long,
@@ -248,8 +248,8 @@ interface LibraryDao {
@Query(
"SELECT * FROM artists WHERE sourceId = :sourceId " +
"AND (:starredFilter = 0 OR (:starredFilter = 1 AND starred IS NOT NULL) OR (:starredFilter = 2 AND starred IS NULL)) " +
"AND (:search = '' OR name LIKE '%' || :search || '%') " +
"ORDER BY albumCount ASC, name COLLATE NOCASE",
"AND (:search = '' OR instr(lower(name), lower(:search)) > 0) " +
"ORDER BY albumCount ASC, name COLLATE NOCASE, id",
)
fun artistsByAlbumCountReversed(
sourceId: Long,
@@ -260,8 +260,8 @@ interface LibraryDao {
@Query(
"SELECT * FROM artists WHERE sourceId = :sourceId " +
"AND (:starredFilter = 0 OR (:starredFilter = 1 AND starred IS NOT NULL) OR (:starredFilter = 2 AND starred IS NULL)) " +
"AND (:search = '' OR name LIKE '%' || :search || '%') " +
"ORDER BY starred IS NULL, starred DESC, name COLLATE NOCASE",
"AND (:search = '' OR instr(lower(name), lower(:search)) > 0) " +
"ORDER BY starred IS NULL, starred DESC, name COLLATE NOCASE, id",
)
fun artistsByStarred(
sourceId: Long,
@@ -272,8 +272,8 @@ interface LibraryDao {
@Query(
"SELECT * FROM artists WHERE sourceId = :sourceId " +
"AND (:starredFilter = 0 OR (:starredFilter = 1 AND starred IS NOT NULL) OR (:starredFilter = 2 AND starred IS NULL)) " +
"AND (:search = '' OR name LIKE '%' || :search || '%') " +
"ORDER BY starred IS NULL, starred ASC, name COLLATE NOCASE",
"AND (:search = '' OR instr(lower(name), lower(:search)) > 0) " +
"ORDER BY starred IS NULL, starred ASC, name COLLATE NOCASE, id",
)
fun artistsByStarredReversed(
sourceId: Long,
@@ -283,8 +283,8 @@ interface LibraryDao {
@Query(
"SELECT * FROM playlists WHERE sourceId = :sourceId " +
"AND (:search = '' OR name LIKE '%' || :search || '%') " +
"ORDER BY name COLLATE NOCASE",
"AND (:search = '' OR instr(lower(name), lower(:search)) > 0) " +
"ORDER BY name COLLATE NOCASE, id",
)
fun playlistsByName(
sourceId: Long,
@@ -293,8 +293,8 @@ interface LibraryDao {
@Query(
"SELECT * FROM playlists WHERE sourceId = :sourceId " +
"AND (:search = '' OR name LIKE '%' || :search || '%') " +
"ORDER BY name COLLATE NOCASE DESC",
"AND (:search = '' OR instr(lower(name), lower(:search)) > 0) " +
"ORDER BY name COLLATE NOCASE DESC, id",
)
fun playlistsByNameReversed(
sourceId: Long,
@@ -303,8 +303,8 @@ interface LibraryDao {
@Query(
"SELECT * FROM playlists WHERE sourceId = :sourceId " +
"AND (:search = '' OR name LIKE '%' || :search || '%') " +
"ORDER BY created DESC, name COLLATE NOCASE",
"AND (:search = '' OR instr(lower(name), lower(:search)) > 0) " +
"ORDER BY created DESC, name COLLATE NOCASE, id",
)
fun playlistsByAdded(
sourceId: Long,
@@ -313,8 +313,8 @@ interface LibraryDao {
@Query(
"SELECT * FROM playlists WHERE sourceId = :sourceId " +
"AND (:search = '' OR name LIKE '%' || :search || '%') " +
"ORDER BY created ASC, name COLLATE NOCASE",
"AND (:search = '' OR instr(lower(name), lower(:search)) > 0) " +
"ORDER BY created ASC, name COLLATE NOCASE, id",
)
fun playlistsByAddedReversed(
sourceId: Long,
@@ -323,8 +323,8 @@ interface LibraryDao {
@Query(
"SELECT * FROM playlists WHERE sourceId = :sourceId " +
"AND (:search = '' OR name LIKE '%' || :search || '%') " +
"ORDER BY changed DESC, name COLLATE NOCASE",
"AND (:search = '' OR instr(lower(name), lower(:search)) > 0) " +
"ORDER BY changed DESC, name COLLATE NOCASE, id",
)
fun playlistsByUpdated(
sourceId: Long,
@@ -333,8 +333,8 @@ interface LibraryDao {
@Query(
"SELECT * FROM playlists WHERE sourceId = :sourceId " +
"AND (:search = '' OR name LIKE '%' || :search || '%') " +
"ORDER BY changed ASC, name COLLATE NOCASE",
"AND (:search = '' OR instr(lower(name), lower(:search)) > 0) " +
"ORDER BY changed ASC, name COLLATE NOCASE, id",
)
fun playlistsByUpdatedReversed(
sourceId: Long,
@@ -347,8 +347,8 @@ interface LibraryDao {
"WHERE songs.sourceId = :sourceId " +
"AND (:starredFilter = 0 OR (:starredFilter = 1 AND songs.starred IS NOT NULL) OR " +
"(:starredFilter = 2 AND songs.starred IS NULL)) " +
"AND (:search = '' OR songs.title LIKE '%' || :search || '%' " +
"OR songs.artist LIKE '%' || :search || '%' OR songs.album LIKE '%' || :search || '%') " +
"AND (:search = '' OR instr(lower(songs.title), lower(:search)) > 0 " +
"OR instr(lower(songs.artist), lower(:search)) > 0 OR instr(lower(songs.album), lower(:search)) > 0) " +
"ORDER BY albums.albumArtist COLLATE NOCASE, songs.album COLLATE NOCASE, songs.disc, songs.track, " +
"songs.title COLLATE NOCASE, songs.id",
)
@@ -364,8 +364,25 @@ interface LibraryDao {
"WHERE songs.sourceId = :sourceId " +
"AND (:starredFilter = 0 OR (:starredFilter = 1 AND songs.starred IS NOT NULL) OR " +
"(:starredFilter = 2 AND songs.starred IS NULL)) " +
"AND (:search = '' OR songs.title LIKE '%' || :search || '%' " +
"OR songs.artist LIKE '%' || :search || '%' OR songs.album LIKE '%' || :search || '%') " +
"AND (:search = '' OR instr(lower(songs.title), lower(:search)) > 0 " +
"OR instr(lower(songs.artist), lower(:search)) > 0 OR instr(lower(songs.album), lower(:search)) > 0) " +
"ORDER BY albums.albumArtist COLLATE NOCASE DESC, songs.album COLLATE NOCASE DESC, songs.disc DESC, " +
"songs.track DESC, songs.title COLLATE NOCASE DESC, songs.id DESC",
)
fun songsByAlbumReversed(
sourceId: Long,
starredFilter: Int,
search: String,
): PagingSource<Int, SongListItem>
@Query(
"SELECT songs.*, albums.coverArt AS coverArt FROM songs " +
"LEFT JOIN albums ON albums.sourceId = songs.sourceId AND albums.id = songs.albumId " +
"WHERE songs.sourceId = :sourceId " +
"AND (:starredFilter = 0 OR (:starredFilter = 1 AND songs.starred IS NOT NULL) OR " +
"(:starredFilter = 2 AND songs.starred IS NULL)) " +
"AND (:search = '' OR instr(lower(songs.title), lower(:search)) > 0 " +
"OR instr(lower(songs.artist), lower(:search)) > 0 OR instr(lower(songs.album), lower(:search)) > 0) " +
"ORDER BY songs.title COLLATE NOCASE, songs.id",
)
fun songsByTitle(
@@ -380,8 +397,8 @@ interface LibraryDao {
"WHERE songs.sourceId = :sourceId " +
"AND (:starredFilter = 0 OR (:starredFilter = 1 AND songs.starred IS NOT NULL) OR " +
"(:starredFilter = 2 AND songs.starred IS NULL)) " +
"AND (:search = '' OR songs.title LIKE '%' || :search || '%' " +
"OR songs.artist LIKE '%' || :search || '%' OR songs.album LIKE '%' || :search || '%') " +
"AND (:search = '' OR instr(lower(songs.title), lower(:search)) > 0 " +
"OR instr(lower(songs.artist), lower(:search)) > 0 OR instr(lower(songs.album), lower(:search)) > 0) " +
"ORDER BY songs.title COLLATE NOCASE DESC, songs.id",
)
fun songsByTitleReversed(
@@ -396,8 +413,8 @@ interface LibraryDao {
"WHERE songs.sourceId = :sourceId " +
"AND (:starredFilter = 0 OR (:starredFilter = 1 AND songs.starred IS NOT NULL) OR " +
"(:starredFilter = 2 AND songs.starred IS NULL)) " +
"AND (:search = '' OR songs.title LIKE '%' || :search || '%' " +
"OR songs.artist LIKE '%' || :search || '%' OR songs.album LIKE '%' || :search || '%') " +
"AND (:search = '' OR instr(lower(songs.title), lower(:search)) > 0 " +
"OR instr(lower(songs.artist), lower(:search)) > 0 OR instr(lower(songs.album), lower(:search)) > 0) " +
"ORDER BY songs.artist COLLATE NOCASE, songs.album COLLATE NOCASE, songs.disc, songs.track, songs.id",
)
fun songsByArtist(
@@ -412,8 +429,8 @@ interface LibraryDao {
"WHERE songs.sourceId = :sourceId " +
"AND (:starredFilter = 0 OR (:starredFilter = 1 AND songs.starred IS NOT NULL) OR " +
"(:starredFilter = 2 AND songs.starred IS NULL)) " +
"AND (:search = '' OR songs.title LIKE '%' || :search || '%' " +
"OR songs.artist LIKE '%' || :search || '%' OR songs.album LIKE '%' || :search || '%') " +
"AND (:search = '' OR instr(lower(songs.title), lower(:search)) > 0 " +
"OR instr(lower(songs.artist), lower(:search)) > 0 OR instr(lower(songs.album), lower(:search)) > 0) " +
"ORDER BY songs.artist COLLATE NOCASE DESC, songs.album COLLATE NOCASE, songs.disc, songs.track, songs.id",
)
fun songsByArtistReversed(
@@ -428,8 +445,8 @@ interface LibraryDao {
"WHERE songs.sourceId = :sourceId " +
"AND (:starredFilter = 0 OR (:starredFilter = 1 AND songs.starred IS NOT NULL) OR " +
"(:starredFilter = 2 AND songs.starred IS NULL)) " +
"AND (:search = '' OR songs.title LIKE '%' || :search || '%' " +
"OR songs.artist LIKE '%' || :search || '%' OR songs.album LIKE '%' || :search || '%') " +
"AND (:search = '' OR instr(lower(songs.title), lower(:search)) > 0 " +
"OR instr(lower(songs.artist), lower(:search)) > 0 OR instr(lower(songs.album), lower(:search)) > 0) " +
"ORDER BY songs.starred IS NULL, songs.starred DESC, songs.title COLLATE NOCASE, songs.id",
)
fun songsByStarred(
@@ -444,8 +461,8 @@ interface LibraryDao {
"WHERE songs.sourceId = :sourceId " +
"AND (:starredFilter = 0 OR (:starredFilter = 1 AND songs.starred IS NOT NULL) OR " +
"(:starredFilter = 2 AND songs.starred IS NULL)) " +
"AND (:search = '' OR songs.title LIKE '%' || :search || '%' " +
"OR songs.artist LIKE '%' || :search || '%' OR songs.album LIKE '%' || :search || '%') " +
"AND (:search = '' OR instr(lower(songs.title), lower(:search)) > 0 " +
"OR instr(lower(songs.artist), lower(:search)) > 0 OR instr(lower(songs.album), lower(:search)) > 0) " +
"ORDER BY songs.starred IS NULL, songs.starred ASC, songs.title COLLATE NOCASE, songs.id",
)
fun songsByStarredReversed(
@@ -460,8 +477,8 @@ interface LibraryDao {
"WHERE songs.sourceId = :sourceId " +
"AND (:starredFilter = 0 OR (:starredFilter = 1 AND songs.starred IS NOT NULL) OR " +
"(:starredFilter = 2 AND songs.starred IS NULL)) " +
"AND (:search = '' OR songs.title LIKE '%' || :search || '%' " +
"OR songs.artist LIKE '%' || :search || '%' OR songs.album LIKE '%' || :search || '%') " +
"AND (:search = '' OR instr(lower(songs.title), lower(:search)) > 0 " +
"OR instr(lower(songs.artist), lower(:search)) > 0 OR instr(lower(songs.album), lower(:search)) > 0) " +
"ORDER BY songs.created DESC, songs.title COLLATE NOCASE, songs.id",
)
fun songsByAdded(
@@ -476,8 +493,8 @@ interface LibraryDao {
"WHERE songs.sourceId = :sourceId " +
"AND (:starredFilter = 0 OR (:starredFilter = 1 AND songs.starred IS NOT NULL) OR " +
"(:starredFilter = 2 AND songs.starred IS NULL)) " +
"AND (:search = '' OR songs.title LIKE '%' || :search || '%' " +
"OR songs.artist LIKE '%' || :search || '%' OR songs.album LIKE '%' || :search || '%') " +
"AND (:search = '' OR instr(lower(songs.title), lower(:search)) > 0 " +
"OR instr(lower(songs.artist), lower(:search)) > 0 OR instr(lower(songs.album), lower(:search)) > 0) " +
"ORDER BY songs.created ASC, songs.title COLLATE NOCASE, songs.id",
)
fun songsByAddedReversed(
@@ -504,7 +521,7 @@ interface LibraryDao {
artistId: String,
): Flow<Artist?>
@Query("SELECT * FROM albums WHERE sourceId = :sourceId AND artistId = :artistId ORDER BY year DESC, name COLLATE NOCASE")
@Query("SELECT * FROM albums WHERE sourceId = :sourceId AND artistId = :artistId ORDER BY year DESC, name COLLATE NOCASE, id")
fun albumsForArtist(
sourceId: Long,
artistId: String,
@@ -157,7 +157,11 @@ class LibraryRepository(
val dao = db.libraryDao()
when (sort) {
SongSort.Album -> {
dao.songs(sourceId, starred.ordinal, search)
if (descending) {
dao.songsByAlbumReversed(sourceId, starred.ordinal, search)
} else {
dao.songs(sourceId, starred.ordinal, search)
}
}
SongSort.Title -> {
@@ -92,6 +92,16 @@ class QueueRepository(
fun songsEntry(sourceId: Long) = QueueEntry(position = 0, sourceId = sourceId, kind = QueueKind.Songs, refId = "")
suspend fun songsIndexOf(
sourceId: Long,
songId: String,
): Long? =
dao
.songIds(sourceId)
.indexOf(songId)
.takeIf { it >= 0 }
?.toLong()
suspend fun replace(
entries: List<QueueEntry>,
shuffleOrder: LongArray? = null,
@@ -136,7 +136,7 @@ internal object IsoDate {
private val timezone = Regex("(Z|[+-]\\d{2}:?\\d{2})$", RegexOption.IGNORE_CASE)
fun parse(value: String): Long? {
val text = value.trim()
val text = value.trim().let { if (it.endsWith("z")) it.dropLast(1) + "Z" else it }
return try {
val zoned = if (timezone.containsMatchIn(text)) text else text + "Z"
OffsetDateTime.parse(zoned).toEpochSecond()
@@ -192,6 +192,16 @@ class PlaybackController(
startPosition: Long,
) = play(listOf(queueRepository.songsEntry(sourceId)), startPosition)
fun playSong(
sourceId: Long,
songId: String,
) {
scope.launch {
val index = queueRepository.songsIndexOf(sourceId, songId) ?: return@launch
play(listOf(queueRepository.songsEntry(sourceId)), index)
}
}
fun shuffleAlbum(
sourceId: Long,
albumId: String,
@@ -173,6 +173,12 @@ fun LibraryRoute(
val listTab = selectedTab.listTab()
val listQuery by viewModel.listQuery(listTab).collectAsStateWithLifecycle()
val search by viewModel.search(listTab).collectAsStateWithLifecycle()
val resetKeys =
LibraryTab.entries.associateWith { tab ->
val query by viewModel.listQuery(tab.listTab()).collectAsStateWithLifecycle()
val term by viewModel.search(tab.listTab()).collectAsStateWithLifecycle()
"${query.sort}|${query.descending}|${query.starred}|$term"
}
LibraryScreen(
selectedTab = selectedTab,
onTabSelected = { selectedTab = it },
@@ -191,6 +197,7 @@ fun LibraryRoute(
onOpenSettings = onOpenSettings,
playingSongId = playingSongId,
listQuery = listQuery,
resetKeys = resetKeys,
sortOptions = sortOptionsFor(selectedTab),
starredSupported = listTab.supportsStarred,
onSortChange = { viewModel.setListQuery(listTab, listQuery.copy(sort = it)) },
@@ -226,13 +233,14 @@ fun LibraryScreen(
onAlbumClick: (Album) -> Unit,
onArtistClick: (Artist) -> Unit,
onPlaylistClick: (Playlist) -> Unit,
onSongClick: (Int) -> Unit,
onSongClick: (String) -> Unit,
onSync: () -> Unit,
onOpenSettings: () -> Unit,
syncing: Boolean = false,
playingSongId: String? = null,
bottomInset: Dp = 0.dp,
listQuery: ListQuery = ListQuery(""),
resetKeys: Map<LibraryTab, Any?> = emptyMap(),
sortOptions: List<SortOption> = emptyList(),
starredSupported: Boolean = false,
onSortChange: (String) -> Unit = {},
@@ -262,6 +270,7 @@ fun LibraryScreen(
if (pagerState.currentPage != selectedTab.ordinal) {
pagerState.animateScrollToPage(selectedTab.ordinal)
}
searchActive = false
}
val density = LocalDensity.current
@@ -279,7 +288,6 @@ fun LibraryScreen(
val statusBarTop = with(density) { WindowInsets.statusBars.getTop(this).toDp() }
val filtersActive = listQuery.starred != StarredFilter.Any || search.isNotEmpty()
val listBottomInset = bottomInset + if (searchActive) SEARCH_BAR_CLEARANCE else FAB_CLEARANCE
val resetKey = listOf(listQuery.sort, listQuery.descending, listQuery.starred, search)
Box(modifier.fillMaxSize()) {
Column(
@@ -327,7 +335,9 @@ fun LibraryScreen(
state = pagerState,
modifier = Modifier.fillMaxWidth().weight(1f),
) { page ->
when (LibraryTab.entries[page]) {
val pageTab = LibraryTab.entries[page]
val resetKey = resetKeys[pageTab]
when (pageTab) {
LibraryTab.Albums -> {
AlbumsContent(
albums,
@@ -129,10 +129,10 @@ class LibraryViewModel(
thumbnail: Boolean,
): CoverArtRef? = sourceRepository.coverArt(coverArt, thumbnail)
fun playSong(position: Int) {
fun playSong(songId: String) {
viewModelScope.launch {
val sourceId = sourceRepository.activeSourceIdOnce() ?: return@launch
playbackController.playSongs(sourceId, position.toLong())
playbackController.playSong(sourceId, songId)
}
}
@@ -47,7 +47,7 @@ fun SongsContent(
items: LazyPagingItems<SongListItem>,
coverArt: (String?, Boolean) -> CoverArtRef?,
bottomInset: Dp,
onSongClick: (Int) -> Unit,
onSongClick: (String) -> Unit,
playingSongId: String? = null,
filtered: Boolean = false,
onClearFilters: () -> Unit = {},
@@ -84,7 +84,7 @@ fun SongsContent(
coverArtId = item.coverArt,
coverArt = coverArt,
isPlaying = item.song.id == playingSongId,
modifier = Modifier.clickable { onSongClick(index) },
modifier = Modifier.clickable { onSongClick(item.song.id) },
)
}
}