Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src-tauri/src/db/queries/albums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::time::{SystemTime, UNIX_EPOCH};
use rusqlite::{params, OptionalExtension, Connection};
use serde::Serialize;

use crate::db::queries::artists::ARTIST_NAME_ORDER_BY;
use crate::error::AppError;

const NEW_ALBUM_THRESHOLD_SECS: i64 = 5 * 86_400;
Expand Down Expand Up @@ -51,7 +52,7 @@ pub fn list_all(
LEFT JOIN album_ratings art ON art.album_id = al.id
WHERE (?1 IS NULL OR al.album_artist_id = ?1)
AND EXISTS (SELECT 1 FROM tracks WHERE album_id = al.id AND is_archived = 0)
ORDER BY ar.sort_name, ar.name, al.year, al.title"
ORDER BY {ARTIST_NAME_ORDER_BY}, al.year, al.title"
)
} else {
format!(
Expand All @@ -64,7 +65,7 @@ pub fn list_all(
WHERE ha.album_id IS NULL
AND (?1 IS NULL OR al.album_artist_id = ?1)
AND EXISTS (SELECT 1 FROM tracks WHERE album_id = al.id AND is_archived = 0)
ORDER BY ar.sort_name, ar.name, al.year, al.title"
ORDER BY {ARTIST_NAME_ORDER_BY}, al.year, al.title"
)
};
let mut stmt = conn.prepare(&sql)?;
Expand Down
31 changes: 28 additions & 3 deletions src-tauri/src/db/queries/artists.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@ use serde::Serialize;

use crate::error::AppError;

/// Orders by artist name (falling back to the not-yet-populated `sort_name`
/// column when set), case-insensitively, with any name starting in a digit
/// or symbol sorting before alphabetic names — matches how a media library
/// is conventionally browsed, rather than SQLite's default byte-wise
/// comparison, which sorts all uppercase letters before any lowercase one.
pub const ARTIST_NAME_ORDER_BY: &str = "\
CASE WHEN COALESCE(ar.sort_name, ar.name) GLOB '[A-Za-z]*' THEN 1 ELSE 0 END, \
COALESCE(ar.sort_name, ar.name) COLLATE NOCASE";

#[derive(Debug, Serialize)]
pub struct ArtistRow {
pub id: i64,
Expand All @@ -14,7 +23,7 @@ pub struct ArtistRow {
/// artist, e.g. a one-off featured artist), since this list drives the
/// sidebar's album-browsing navigation.
pub fn list_album_artists(conn: &Connection) -> Result<Vec<ArtistRow>, AppError> {
let mut stmt = conn.prepare(
let sql = format!(
"SELECT ar.id, ar.name, COUNT(al.id) as album_count
FROM artists ar
JOIN albums al ON al.album_artist_id = ar.id
Expand All @@ -23,8 +32,9 @@ pub fn list_album_artists(conn: &Connection) -> Result<Vec<ArtistRow>, AppError>
AND EXISTS (SELECT 1 FROM tracks WHERE album_id = al.id AND is_archived = 0)
GROUP BY ar.id
HAVING COUNT(al.id) > 0
ORDER BY ar.sort_name, ar.name",
)?;
ORDER BY {ARTIST_NAME_ORDER_BY}"
);
let mut stmt = conn.prepare(&sql)?;
let rows = stmt.query_map([], |row| {
Ok(ArtistRow {
id: row.get(0)?,
Expand Down Expand Up @@ -106,6 +116,21 @@ mod tests {
assert_eq!(names, vec!["Visible Artist"]);
}

#[test]
fn list_album_artists_sorts_case_insensitively_with_symbols_before_letters() {
let conn = test_connection();
let names = ["zebra", "Apple", "!!!", "banana", "3OH!3", "Aardvark"];
for (index, name) in names.iter().enumerate() {
let artist_id = upsert(&conn, name).unwrap();
let album_id = albums::upsert(&conn, "Album", artist_id, Some(2000 + index as i64)).unwrap();
insert_active_track(&conn, &format!("/music/{index}.flac"), artist_id, album_id);
}

let rows = list_album_artists(&conn).unwrap();
let names: Vec<&str> = rows.iter().map(|r| r.name.as_str()).collect();
assert_eq!(names, vec!["!!!", "3OH!3", "Aardvark", "Apple", "banana", "zebra"]);
}

#[test]
fn list_album_artists_excludes_artists_with_no_active_tracks() {
let conn = test_connection();
Expand Down
3 changes: 2 additions & 1 deletion src-tauri/src/db/queries/tracks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::collections::HashMap;
use rusqlite::{params, Connection};
use serde::Serialize;

use crate::db::queries::artists::ARTIST_NAME_ORDER_BY;
use crate::error::AppError;

pub struct NewTrack<'a> {
Expand Down Expand Up @@ -250,7 +251,7 @@ pub fn list_all(conn: &Connection) -> Result<Vec<TrackRow>, AppError> {
LEFT JOIN artists ar ON ar.id = t.track_artist_id
LEFT JOIN albums al ON al.id = t.album_id
WHERE t.is_archived = 0
ORDER BY ar.sort_name, ar.name, al.title, t.disc_no, t.track_no"
ORDER BY {ARTIST_NAME_ORDER_BY}, al.title, t.disc_no, t.track_no"
);
let mut stmt = conn.prepare(&sql)?;
let rows = stmt.query_map([], track_row_from)?;
Expand Down
Loading