Fixed artist page sorting

This commit is contained in:
Ultradesu
2026-07-23 14:23:22 +03:00
parent 0b2eaa1496
commit 2128cd2300
3 changed files with 164 additions and 149 deletions
Generated
+39 -138
View File
@@ -933,6 +933,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453"
dependencies = [
"hybrid-array",
"rand_core 0.10.1",
]
[[package]]
@@ -965,9 +966,9 @@ dependencies = [
[[package]]
name = "curve25519-dalek"
version = "5.0.0-rc.0"
version = "5.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f359e08ca85e7bd759e1fd933ff2bccd81864c60a8fba0e259c7f822b0924bf"
checksum = "b5eed333089e2e1c1ac8c6c0398e5e2497b4c9926ca6d0365ed1e099afa5bc23"
dependencies = [
"cfg-if",
"cpufeatures 0.3.0",
@@ -992,38 +993,14 @@ dependencies = [
"syn 2.0.119",
]
[[package]]
name = "darling"
version = "0.20.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee"
dependencies = [
"darling_core 0.20.11",
"darling_macro 0.20.11",
]
[[package]]
name = "darling"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d"
dependencies = [
"darling_core 0.23.0",
"darling_macro 0.23.0",
]
[[package]]
name = "darling_core"
version = "0.20.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e"
dependencies = [
"fnv",
"ident_case",
"proc-macro2",
"quote",
"strsim",
"syn 2.0.119",
"darling_core",
"darling_macro",
]
[[package]]
@@ -1039,24 +1016,13 @@ dependencies = [
"syn 2.0.119",
]
[[package]]
name = "darling_macro"
version = "0.20.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead"
dependencies = [
"darling_core 0.20.11",
"quote",
"syn 2.0.119",
]
[[package]]
name = "darling_macro"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d"
dependencies = [
"darling_core 0.23.0",
"darling_core",
"quote",
"syn 2.0.119",
]
@@ -1127,37 +1093,6 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "derive_builder"
version = "0.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947"
dependencies = [
"derive_builder_macro",
]
[[package]]
name = "derive_builder_core"
version = "0.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8"
dependencies = [
"darling 0.20.11",
"proc-macro2",
"quote",
"syn 2.0.119",
]
[[package]]
name = "derive_builder_macro"
version = "0.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c"
dependencies = [
"derive_builder_core",
"syn 2.0.119",
]
[[package]]
name = "derive_more"
version = "2.1.1"
@@ -1296,9 +1231,9 @@ dependencies = [
[[package]]
name = "ed25519-dalek"
version = "3.0.0-rc.0"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b011170fe4f04665565b4110afef66774fe9ffff278f3eb5b81cc73d26e27d60"
checksum = "6ebaa1a2bf1290ab3bfe5a7b771d050ebffab2711c19a81691c683a5144a25de"
dependencies = [
"curve25519-dalek",
"ed25519",
@@ -1493,7 +1428,7 @@ dependencies = [
[[package]]
name = "federation-net"
version = "0.1.0"
source = "git+https://gt.hexor.cy/ab/frid.git#8ee1db9cf89ea604c6b8a8c3fe089a714c1e321f"
source = "git+https://gt.hexor.cy/ab/frid.git#daea24042c01e8a69e7e9a90fc7784dade337bcc"
dependencies = [
"blake3",
"data-encoding",
@@ -2375,7 +2310,7 @@ version = "0.3.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5eb2d60ef19920a3a9193c3e371f726ec1dafc045dac788d0fb3704272458971"
dependencies = [
"darling 0.23.0",
"darling",
"indoc",
"proc-macro2",
"quote",
@@ -2426,9 +2361,9 @@ dependencies = [
[[package]]
name = "iroh"
version = "1.0.2"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fca9b4b462c343ff88fc0af4096c186f939b602a0bc08723536ef2c31c93971"
checksum = "460de6bc52163b41b1646931f2897e5ab986f0966ade444467fec25024751a72"
dependencies = [
"backon",
"blake3",
@@ -2477,9 +2412,9 @@ dependencies = [
[[package]]
name = "iroh-base"
version = "1.0.2"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830a582cd54410dc1aa71d4786a82c3297d7b0165accd8b6dbbb3b240b48140d"
checksum = "6be73e16ee21c923aca9b3121aaa0db936f7c7ecc156ff47b8dac944c68d59a8"
dependencies = [
"curve25519-dalek",
"data-encoding",
@@ -2496,9 +2431,9 @@ dependencies = [
[[package]]
name = "iroh-dns"
version = "1.0.2"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "516e4eedc38e33ab69a6bd325520332dc3d67b25454e2d590ebb84a25240dd9a"
checksum = "46f6a9b39d18e6345f5c151afd299f2488e2cb5c520fe41b107b6bd3dc4c3349"
dependencies = [
"arc-swap",
"cfg_aliases",
@@ -2547,9 +2482,9 @@ dependencies = [
[[package]]
name = "iroh-relay"
version = "1.0.2"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8149bb6a57126225a07d6928846d82dcedfd24ea0f863ef7b2eb475e1d726354"
checksum = "24bd586cf927f7b700f56ec3639b53cb5fa901ce284784051ff71092bfbf8193"
dependencies = [
"blake3",
"bytes",
@@ -2586,7 +2521,6 @@ dependencies = [
"tokio-websockets",
"tracing",
"url",
"vergen-gitcl",
"webpki-roots",
"ws_stream_wasm",
]
@@ -2730,9 +2664,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "libc"
version = "0.2.186"
version = "0.2.188"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
checksum = "22053b6a34f84abc97f9129e61334f40174659a1b9bd18c970b83db6a9a6348b"
[[package]]
name = "libm"
@@ -3027,7 +2961,7 @@ dependencies = [
[[package]]
name = "music-dht"
version = "0.1.0"
source = "git+https://gt.hexor.cy/ab/frid.git#8ee1db9cf89ea604c6b8a8c3fe089a714c1e321f"
source = "git+https://gt.hexor.cy/ab/frid.git#daea24042c01e8a69e7e9a90fc7784dade337bcc"
dependencies = [
"async-trait",
"blake3",
@@ -3275,9 +3209,9 @@ dependencies = [
[[package]]
name = "noq"
version = "1.0.1"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4bf95190af1bd4a00a10e8255ca0c8ddd9e9a9f5e79151d7a7eb6d56aff5dc89"
checksum = "e11803df44ac03a30988d61585ea50885d5428e42da944fe1e498799da7886a2"
dependencies = [
"bytes",
"cfg_aliases",
@@ -3297,9 +3231,9 @@ dependencies = [
[[package]]
name = "noq-proto"
version = "1.0.1"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa6c890013591e709a3e45dd53501351b7e27e7ff3c7e9fc3dce43e300e7e9d3"
checksum = "334c3c9833f7b2c573cceb9896ddc7aaeb58c8807cbb63211b24d1fe88bf866e"
dependencies = [
"aes-gcm",
"bytes",
@@ -3324,9 +3258,9 @@ dependencies = [
[[package]]
name = "noq-udp"
version = "1.0.1"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3137a52df66c20090a889828d1c655f21f52294cba64e5c4fbb04fc83eee7c8e"
checksum = "bde7a5d5102f1cff03d482240f0ed20551661f63663620f4b26112ed751165e9"
dependencies = [
"cfg_aliases",
"libc",
@@ -4836,6 +4770,9 @@ name = "signature"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28d567dcbaf0049cb8ac2608a76cd95ff9e4412e1899d389ee400918ca7537f5"
dependencies = [
"rand_core 0.10.1",
]
[[package]]
name = "simd-adler32"
@@ -5454,9 +5391,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tokio"
version = "1.53.0"
version = "1.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d988bcd52dbe076d3d46903332f58c912b87a2c49b1428419a5845154762ffee"
checksum = "202caea871b69668250d242070849eb495be178ed697a3e98aebce5bc81a0bed"
dependencies = [
"bytes",
"libc",
@@ -5503,14 +5440,15 @@ dependencies = [
[[package]]
name = "tokio-util"
version = "0.7.18"
version = "0.7.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098"
checksum = "494815d09bf52b5548659851081238f0ca39ff638363907596da739561c62c52"
dependencies = [
"bytes",
"futures-core",
"futures-sink",
"futures-util",
"libc",
"pin-project-lite",
"tokio",
]
@@ -5851,43 +5789,6 @@ version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "vergen"
version = "9.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b849a1f6d8639e8de261e81ee0fc881e3e3620db1af9f2e0da015d4382ceaf75"
dependencies = [
"anyhow",
"derive_builder",
"rustversion",
"vergen-lib",
]
[[package]]
name = "vergen-gitcl"
version = "9.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77ff3b5300a085d6bcd8fc96a507f706a28ae3814693236c9b409db71a1d15b9"
dependencies = [
"anyhow",
"derive_builder",
"rustversion",
"time",
"vergen",
"vergen-lib",
]
[[package]]
name = "vergen-lib"
version = "9.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b34a29ba7e9c59e62f229ae1932fb1b8fb8a6fdcc99215a641913f5f5a59a569"
dependencies = [
"anyhow",
"derive_builder",
"rustversion",
]
[[package]]
name = "version_check"
version = "0.9.5"
@@ -6697,18 +6598,18 @@ dependencies = [
[[package]]
name = "zerocopy"
version = "0.8.54"
version = "0.8.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7cbbc0a705a0fd05cc3676525980d2bf5a9bc4adac6d6475209a7887cf59d19"
checksum = "b5a105cd7b140f6eeec8acff2ea38135d3cab283ada58540f629fe51e46696eb"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.54"
version = "0.8.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2e817b7b52d0c7358d3246da9d69935ebb18116b2b102b4230dac079b4862f5"
checksum = "0fe976fb70c78cd64cccfe3a6fc142244e8a77b70959b30faf9d0ac37ee228eb"
dependencies = [
"proc-macro2",
"quote",
+90 -8
View File
@@ -121,17 +121,23 @@ impl Default for GlobalTab {
}
/// Releases of an artist in display order: grouped by type (albums, singles,
/// EPs, compilations, then anything else), keeping server order within a
/// group. Returns (group label, indices into the original slice). Cursor
/// positions use this flattened order, so update() and ui must both go
/// through here.
/// EPs, compilations, then anything else), newest first within each group.
/// Returns (group label, indices into the original slice). Cursor positions
/// use this flattened order, so update() and ui must both go through here.
pub fn release_groups(releases: &[ReleaseCard]) -> Vec<(&'static str, Vec<usize>)> {
release_type_groups(releases, |release| &release.release_type)
release_type_groups(
releases,
|release| &release.release_type,
|release| release.year,
|release| &release.title,
)
}
fn release_type_groups<T>(
items: &[T],
release_type: impl Fn(&T) -> &str,
release_year: impl Fn(&T) -> Option<i32>,
release_title: impl Fn(&T) -> &str,
) -> Vec<(&'static str, Vec<usize>)> {
const GROUPS: [(&str, &str); 4] = [
("album", "Albums"),
@@ -141,24 +147,40 @@ fn release_type_groups<T>(
];
let mut groups: Vec<(&'static str, Vec<usize>)> = Vec::new();
for (kind, label) in GROUPS {
let indices: Vec<usize> = items
let mut indices: Vec<usize> = items
.iter()
.enumerate()
.filter(|(_, item)| release_type(item).eq_ignore_ascii_case(kind))
.map(|(i, _)| i)
.collect();
sort_release_indices(&mut indices, items, &release_year, &release_title);
if !indices.is_empty() {
groups.push((label, indices));
}
}
let known: Vec<usize> = groups.iter().flat_map(|(_, v)| v.iter().copied()).collect();
let other: Vec<usize> = (0..items.len()).filter(|i| !known.contains(i)).collect();
let mut other: Vec<usize> = (0..items.len()).filter(|i| !known.contains(i)).collect();
sort_release_indices(&mut other, items, &release_year, &release_title);
if !other.is_empty() {
groups.push(("Other", other));
}
groups
}
fn sort_release_indices<T>(
indices: &mut [usize],
items: &[T],
release_year: &impl Fn(&T) -> Option<i32>,
release_title: &impl Fn(&T) -> &str,
) {
indices.sort_by(|&left, &right| {
release_year(&items[right])
.unwrap_or(i32::MIN)
.cmp(&release_year(&items[left]).unwrap_or(i32::MIN))
.then_with(|| release_title(&items[left]).cmp(release_title(&items[right])))
});
}
/// Flattened display order of releases (concatenated groups).
pub fn release_display_order(releases: &[ReleaseCard]) -> Vec<usize> {
release_groups(releases)
@@ -177,7 +199,12 @@ pub fn release_rows(releases: &[ReleaseCard], columns: usize) -> Vec<Vec<usize>>
pub fn fed_release_groups(
releases: &[crate::federation::FedRelease],
) -> Vec<(&'static str, Vec<usize>)> {
release_type_groups(releases, |release| &release.release_type)
release_type_groups(
releases,
|release| &release.release_type,
|release| release.year,
|release| &release.title,
)
}
/// Flattened display order of federated releases (concatenated groups).
@@ -212,6 +239,61 @@ fn grouped_release_rows(
rows
}
#[cfg(test)]
mod tests {
use super::*;
fn release(id: i64, title: &str, release_type: &str, year: Option<i32>) -> ReleaseCard {
ReleaseCard {
id,
title: title.to_string(),
release_type: release_type.to_string(),
year,
cover_path: None,
track_count: 1,
}
}
fn fed_release(
title: &str,
release_type: &str,
year: Option<i32>,
) -> crate::federation::FedRelease {
crate::federation::FedRelease {
title: title.to_string(),
release_type: release_type.to_string(),
year,
..Default::default()
}
}
#[test]
fn release_display_order_is_newest_first_within_each_type() {
let releases = vec![
release(1, "Old Album", "album", Some(1991)),
release(2, "New Single", "single", Some(2024)),
release(3, "New Album", "album", Some(2020)),
release(4, "Undated Album", "album", None),
release(5, "Old Single", "single", Some(1999)),
];
assert_eq!(release_display_order(&releases), vec![2, 0, 3, 1, 4]);
}
#[test]
fn fed_release_display_order_is_newest_first_within_each_type() {
let releases = vec![
fed_release("Old Album", "album", Some(1991)),
fed_release("New Single", "single", Some(2024)),
fed_release("New Album", "album", Some(2020)),
fed_release("Undated Album", "album", None),
fed_release("Old Single", "single", Some(1999)),
];
assert_eq!(fed_release_display_order(&releases), vec![2, 0, 3, 1, 4]);
}
}
/// The virtual Likes playlist id (`kind == "likes"`).
pub use crate::library::LIKES_PLAYLIST_ID;
+35 -3
View File
@@ -605,9 +605,9 @@ pub fn merge_catalogs(name: &str, catalogs: Vec<(String, CatalogArtist)>) -> Fed
.then_with(|| a.track.title.cmp(&b.track.title))
});
releases.sort_by(|a, b| {
a.year
.unwrap_or(i32::MAX)
.cmp(&b.year.unwrap_or(i32::MAX))
b.year
.unwrap_or(i32::MIN)
.cmp(&a.year.unwrap_or(i32::MIN))
.then_with(|| a.title.cmp(&b.title))
});
@@ -754,4 +754,36 @@ mod tests {
assert_eq!(card.appears_on[0].track.artists, vec!["Host"]);
assert_eq!(card.appears_on[0].track.featured_artists, vec!["Guest"]);
}
#[test]
fn merge_catalogs_sorts_releases_newest_first() {
let release = |title: &str, year: Option<i32>, item: &str| CatalogRelease {
title: title.into(),
release_type: "album".into(),
year,
tracks: vec![track("Song", 1, item)],
};
let card = merge_catalogs(
"Metallica",
vec![(
"peer-a".to_string(),
CatalogArtist {
name: "Metallica".into(),
releases: vec![
release("Old Album", Some(1991), "a1"),
release("New Album", Some(2024), "a2"),
release("Undated Album", None, "a3"),
],
appears_on: Vec::new(),
},
)],
);
let titles: Vec<&str> = card
.releases
.iter()
.map(|release| release.title.as_str())
.collect();
assert_eq!(titles, vec!["New Album", "Old Album", "Undated Album"]);
}
}