Skip to content

Commit 1bdf9f6

Browse files
committed
Implement "Ignore The" setting for artist sorting
- Introduced `SortRanks` struct to precompute alphabetical ranks for songs, artists, and albums. - Added `ignore_the` field to `Library` and `App` structs to manage user preference for ignoring leading "The" in artist names. - Updated `Library` to sort artists and album groups based on the `ignore_the` setting. - Implemented `prepare_order` method to rebuild ranks and sort artists when the library is set or the setting changes. - Created `collate` module to handle custom alphabetical comparisons, including handling of leading "The" in artist names. - Updated settings UI to include "Ignore The" option, reflecting current state and allowing toggling. - Added tests to ensure correct behavior of sorting with and without the "Ignore The" setting.
1 parent 0a5a172 commit 1bdf9f6

14 files changed

Lines changed: 668 additions & 79 deletions

File tree

CHANGELOG.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,28 @@ level the commit history supports; from `v0.1.6` onward, entries are written as
1616

1717
## [Unreleased]
1818

19+
### Fixed
20+
21+
- **The Library sorted lowercase and accented names below "Z".** *Host-tested against names from
22+
the reference library; device-unverified.* Every list compared names byte by byte, so every
23+
capital came before every lowercase letter and every accented letter after `z`: 12 of 310
24+
artists (`alt‐J`, `bôa`, `julie`, `the north` …) and 127 song titles sat under `Zola Jesus`,
25+
while the A–Z rail filed them under their letters. Artists, albums, songs, folders and playlists
26+
now share one order: case-blind, accents folded (`bôa` beside `Boa`, `Édith` among the E's),
27+
names starting with a digit or punctuation first and other scripts after Z, as Sony's own
28+
scanner groups them. The rail uses the same folding, so `bôa` and `Édith` are reachable from B
29+
and E. The rail also used to file "The Beatles" under B while the list sorted it under T; the two
30+
now agree, and the setting below decides which.
31+
32+
### Added
33+
34+
- **Settings ▸ Ignore "The" in artists.** *Host-tested; device-unverified.* Off by default, so
35+
artists sort as written. On, "The Beatles" sorts among the B's — in the Artists tab, the Albums
36+
tab's artist groups and Songs sorted by artist, and on the A–Z rail — the way Sony's own player
37+
files it. Song titles and album names keep their "The" either way. The lists re-sort the moment
38+
it is switched, an open artist or album page stays on what it was showing, and Reset settings
39+
turns it off.
40+
1941
## [0.3.5] — 2026-09-14
2042

2143
### Added

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
[![device](https://img.shields.io/badge/device-NW--A55%20%2F%20A50%20series-blue)](docs/baseline_v1.4.md)
88

99
**A replacement music player for the Sony NW-A55 Walkman** (and its NW-A50-series siblings).
10-
This is a personal project that I made for my self if people want to tweak it and adapt it to their personal use, please do so but I can't confirm any features or time for this project. If you have issues please raise them in the issues section but I can't promise that issues will be patched. Please use https://github.com/unknown321/wbrt before you install Cinder And if you have the time and skill please contribute with pull requests. I have deliberately not included any way to sponsor or pay for this project. Not least because I want it to be free, but because I don't want the obligation for support that comes with it, as said before I made this for me. The single best way to support is to test, feedback and patch.
10+
This is a personal project that I made for myself if people want to tweak it and adapt it to their personal use, please do so but I can't confirm any features or time for this project. If you have issues please raise them in the issues section but I can't promise that issues will be patched. Please use https://github.com/unknown321/wbrt before you install Cinder and if you have the time and skill please contribute with pull requests. I have deliberately not included any way to sponsor or pay for this project. Not least because I want it to be free, but because I don't want the obligation for support that comes with it, as said before I made this for me. The single best way to support is to test, feedback and patch.
11+
12+
Currently Cinder is still in testing and development despite what github may say.
1113

1214
You install it from your computer over the USB cable, in one program, in about a minute. The player
1315
then starts up into Cinder instead of Sony's music app. Same device, same headphone jack, same

player/cinder-ffi/src/lib.rs

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -904,6 +904,7 @@ fn settings_body(r: &Render) -> String {
904904
// nothing on screen explains.
905905
body.push_str(&format!("bt_fine={}\n", r.app.bt_fine_span()));
906906
body.push_str(&format!("volume_limit={}\n", r.app.volume_limit() as u8));
907+
body.push_str(&format!("ignore_the={}\n", r.app.ignore_the() as u8));
907908
// The palette by id — the CHOICE, not what happens to be drawn. If the folder could not be read
908909
// this boot, Cinder is on screen, but the palette the user picked is still the one to keep.
909910
body.push_str(&format!("palette={}\n", r.app.palette_id()));
@@ -1262,7 +1263,11 @@ fn build_library(db: &cinder_db::Db) -> cinder_ui::Library {
12621263
});
12631264
}
12641265

1265-
album_rows.sort_by(|x, y| x.artist.cmp(&y.artist).then_with(|| x.name.cmp(&y.name)));
1266+
// One alphabetical order for the whole Library (cinder_ui::collate): byte order put every
1267+
// lowercase or accented name after "Z" — `alt‐J`, `bôa`, `the north` below `Zola Jesus`.
1268+
album_rows.sort_by(|x, y| {
1269+
cinder_ui::collate::cmp(&x.artist, &y.artist).then_with(|| cinder_ui::collate::cmp(&x.name, &y.name))
1270+
});
12661271
let mut album_groups: Vec<ArtistGroup> = Vec::new();
12671272
for ar in album_rows {
12681273
match album_groups.last_mut() {
@@ -1285,7 +1290,7 @@ fn build_library(db: &cinder_db::Db) -> cinder_ui::Library {
12851290
ArtistRow { albums: albs.len() as u32, tracks: tr, arts, album_ids, name }
12861291
})
12871292
.collect();
1288-
artists.sort_by(|a, b| a.name.cmp(&b.name));
1293+
artists.sort_by(|a, b| cinder_ui::collate::cmp(&a.name, &b.name));
12891294

12901295
// Playlists: real ones from the DB (Sony keeps them as containers in a second object tree —
12911296
// see Db::playlists). Empty-but-honest if the DB has none, rather than sample data.
@@ -1372,6 +1377,9 @@ fn build_library(db: &cinder_db::Db) -> cinder_ui::Library {
13721377
hires_tracks,
13731378
filter_genre: None,
13741379
filter_hires: false,
1380+
// The App's preference, applied by `set_library` (which also re-sorts the artists).
1381+
ignore_the: false,
1382+
ranks: Default::default(),
13751383
folders,
13761384
folder_roots,
13771385
}
@@ -1461,14 +1469,14 @@ fn build_folders(
14611469

14621470
// Tracks in filename order — on a music folder that is track order, and it is the order the
14631471
// files are actually in on the volume, which is the whole point of browsing this way.
1464-
// Subdirectories alphabetically, case-insensitively.
1465-
let names: Vec<String> = out.iter().map(|f| f.name.to_lowercase()).collect();
1472+
// Subdirectories alphabetically, in the Library's one collation (case and accents folded).
1473+
let names: Vec<String> = out.iter().map(|f| f.name.clone()).collect();
14661474
for f in out.iter_mut() {
14671475
f.tracks.sort_by(|a, b| a.track.cmp(&b.track).then_with(|| a.title.cmp(&b.title)));
14681476
}
14691477
for i in 0..out.len() {
14701478
let mut subs = std::mem::take(&mut out[i].subdirs);
1471-
subs.sort_by(|a, b| names[*a].cmp(&names[*b]));
1479+
subs.sort_by(|a, b| cinder_ui::collate::cmp(&names[*a], &names[*b]));
14721480
// Prune the branches that hold no music at all.
14731481
subs.retain(|s| out[*s].total > 0);
14741482
out[i].subdirs = subs;
@@ -1477,7 +1485,7 @@ fn build_folders(
14771485
let mut roots: Vec<usize> = (0..out.len())
14781486
.filter(|i| out[*i].parent.is_none() && out[*i].total > 0)
14791487
.collect();
1480-
roots.sort_by(|a, b| names[*a].cmp(&names[*b]));
1488+
roots.sort_by(|a, b| cinder_ui::collate::cmp(&names[*a], &names[*b]));
14811489
eprintln!("cinder-ffi: folders: {} dirs, {} root(s)", out.len(), roots.len());
14821490
(out, roots)
14831491
}
@@ -2576,7 +2584,7 @@ fn merge_playlist_rows(
25762584
rows.push(row);
25772585
}
25782586
}
2579-
rows.sort_by(|a, b| a.name.to_lowercase().cmp(&b.name.to_lowercase()));
2587+
rows.sort_by(|a, b| cinder_ui::collate::cmp(&a.name, &b.name));
25802588
rows
25812589
}
25822590

@@ -4803,6 +4811,9 @@ pub extern "C" fn cinder_settings_load(path: *const c_char) -> libc::c_int {
48034811
r.app.set_volume_limit(n != 0);
48044812
}
48054813
}
4814+
// Settings ▸ Ignore "The" in artists. Read before the library arrives, and
4815+
// `set_library` applies it to whatever library comes next.
4816+
"ignore_the" => r.app.set_ignore_the(v == "1"),
48064817
"viz_scale" => {
48074818
if let Ok(n) = v.parse::<u8>() {
48084819
r.app.set_viz_scale(n);

player/cinder-ffi/src/playlists.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ impl Store {
139139
}
140140
}
141141
}
142-
lists.sort_by(|a, b| a.name.to_lowercase().cmp(&b.name.to_lowercase()));
142+
lists.sort_by(|a, b| cinder_ui::collate::cmp(&a.name, &b.name));
143143
Store { dir: primary_dir, lists }
144144
}
145145

@@ -225,8 +225,10 @@ impl Store {
225225
.collect()
226226
}
227227

228+
// The Library's one collation, so the store's order is the order the Playlists tab draws and
229+
// the A–Z rail files (`cinder_ui::collate`).
228230
fn sort(&mut self) {
229-
self.lists.sort_by(|a, b| a.name.to_lowercase().cmp(&b.name.to_lowercase()));
231+
self.lists.sort_by(|a, b| cinder_ui::collate::cmp(&a.name, &b.name));
230232
}
231233
}
232234

player/cinder-host/golden.txt

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ f98435b1ace39bf7 eq_day
4343
1892fa715456f512 sound_setup_b_day
4444
cd6841fdd8d2c783 clockset_day
4545
6a1aefd18a1932ec sound_balance_day
46-
b757866f97753c0d settings_day
46+
82231cdf27b64680 settings_day
4747
aa5a1c551b1762a9 genre_picker_day
4848
f5715a4d225a91fa library_songs_filtered_day
4949
1502b690f7f1b536 folders_root_day
@@ -104,7 +104,7 @@ e4911976c094a2c8 eq_night
104104
1c1bfcfd32217e18 sound_setup_b_night
105105
f6e24799f4cdf803 clockset_night
106106
db181b1945b9020b sound_balance_night
107-
c2bf724a7c2d5fb9 settings_night
107+
dc24f5faafe60e6e settings_night
108108
6a18465a7a81b920 genre_picker_night
109109
b6a9b0efdb4cec0e library_songs_filtered_night
110110
0fe1ff330af32285 folders_root_night
@@ -129,7 +129,7 @@ efdce2c9ec2ef5bd usbdac_night
129129
2513c2d1b1efb6b5 viz_size_2_full
130130
b01e5caaf099a7fa confirm_restart
131131
61de0b6bb868689c confirm_poweroff
132-
523328c49ba0f4ec settings_scrolled
132+
d48d881accbefdc9 settings_scrolled
133133
b1c6037a0bb75403 device
134134
a217e5344a9e434d device_scrolled
135135
4ad380282293f523 device_degraded
@@ -163,24 +163,24 @@ bd6e669fedc7cf66 vizset_peaks_fixed
163163
b9a97b23b9198a50 viz_below_line
164164
e22dec69c4d7c5ff viz_below_pulse
165165
2c0314c25f0e38bc accent_amber_now_playing
166-
aa5b5e59b3335327 accent_amber_settings
166+
caeca4eeae99eca2 accent_amber_settings
167167
4b1a6e1db4f45174 accent_crimson_now_playing
168-
a91fe35ca77a6c34 accent_crimson_settings
168+
a6ed1fd0b8aba8ed accent_crimson_settings
169169
2e87fdcce69b627c accent_violet_now_playing
170-
c12219faff49bce1 accent_violet_settings
170+
7642f2165ec36edc accent_violet_settings
171171
b3d715d20e39116c accent_azure_now_playing
172-
413196aa63a22b8e accent_azure_settings
172+
e2a25e5067fe92af accent_azure_settings
173173
201618fbc75eaf26 accent_mint_now_playing
174-
489398c6560f16c5 accent_mint_settings
174+
60ebe95bac550598 accent_mint_settings
175175
6e47e9c74d6ea2e2 accent_bone_now_playing
176-
ed2fd901b9f86bb7 accent_bone_settings
176+
2766fe0cfe5d1652 accent_bone_settings
177177
950663098198cf80 nav_0_now_playing
178178
7507a446e38aa74b nav_1_menu
179179
4ed2d57de58b2cef nav_2_menu_library
180180
96d931c6aeb6c94b nav_3_library
181181
6097e596446dac6f nav_4_library_artists
182182
4db6947c09339d52 scroll_library_songs
183-
59b7a209c0b3b90a scroll_library_albums
183+
e7bf7d5f462eb251 scroll_library_albums
184184
1aacaf1dd42cc795 i18n_library_songs
185185
29f277427d94f178 i18n_now_playing
186186
65ed9dc76be06083 overlay_volume
@@ -199,31 +199,31 @@ a17b707301f1a525 swipe_queue_4_100px_armed
199199
6bb6667e3136e960 swipe_queue_5_132px_armed
200200
7d9c1517c288dd52 swipe_play_next_5_132px_armed
201201
e2a6d3fb484a6438 uiscale_80_library
202-
eb9bfa488b526fcc uiscale_80_settings
202+
390c831c3e04e21e uiscale_80_settings
203203
12533bfd3cff48d8 uiscale_80_bluetooth
204204
603117598a3237e2 uiscale_80_sound
205205
41485b1df94b899e uiscale_80_nowplaying
206206
a9150951fefcb8f7 uiscale_80_upnext
207207
5bd5ad888039c59d uiscale_80_eq
208208
a7a38af4962b65b7 uiscale_80_settings_bottom
209209
96d931c6aeb6c94b uiscale_100_library
210-
205a0f47987d861a uiscale_100_settings
210+
6958507a41fa4abb uiscale_100_settings
211211
0eab57c3c45f81cc uiscale_100_bluetooth
212212
ed14e37e73110b2a uiscale_100_sound
213213
950663098198cf80 uiscale_100_nowplaying
214214
21984b2bcae0cbd5 uiscale_100_upnext
215215
f92e7f91efbc19c9 uiscale_100_eq
216216
3537cb2a4c274d6d uiscale_100_settings_bottom
217217
982222442288461d uiscale_120_library
218-
4d59d4b01107f4ef uiscale_120_settings
218+
f6b8864cb39e1f2b uiscale_120_settings
219219
0769db74dc7da4e0 uiscale_120_bluetooth
220220
17a4f24c816cbbec uiscale_120_sound
221221
14a04bb6d1b88759 uiscale_120_nowplaying
222222
14c1bfb63cb798f8 uiscale_120_upnext
223223
9b246e62c0d9b49e uiscale_120_eq
224224
18824b21a452b671 uiscale_120_settings_bottom
225225
1cf5d645a1e62bb0 uiscale_140_library
226-
67fd0c56dc5b7145 uiscale_140_settings
226+
6cf22e8f8ec45403 uiscale_140_settings
227227
d97db347a317e2c6 uiscale_140_bluetooth
228228
522dffeced958ff6 uiscale_140_sound
229229
ba74ab1d7f4c1026 uiscale_140_nowplaying

player/cinder-host/src/main.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,7 @@ fn render_all(out: &mut dyn FnMut(&str, &Canvas), opts: &Opts) {
389389
sound::render(c, &theme, &fonts, &s, sound::ROW_BALANCE, 0)
390390
}),
391391
("settings", &|c: &mut Canvas| settings::render(c, &theme, &fonts, 1, 0,
392-
&settings::SettingsView { volume_limit: false, night: theme.night, viz_name: "BARS · VEIL", usb_dac: false, battery_care: true, device: "99% · 34.4 °C",
392+
&settings::SettingsView { ignore_the: false, volume_limit: false, night: theme.night, viz_name: "BARS · VEIL", usb_dac: false, battery_care: true, device: "99% · 34.4 °C",
393393
database: "3,424 tracks", storage: "12.4 / 58 GB", sleep: "30 MIN", brightness: "4 / 5", screen_off: "OFF", auto_off: "OFF", boot_stock: "SONY", clock: "17 Aug · 09:01", accent: cinder_ui::Accent::Amber, palette: pal_name, accent_locked: pal_locked })),
394394
// The genre FILTER, both halves: the picker, and what a filtered Songs list looks like.
395395
// The shuffle band's caption has to follow the filter — shuffling a filtered list
@@ -579,7 +579,7 @@ fn render_all(out: &mut dyn FnMut(&str, &Canvas), opts: &Opts) {
579579
(cinder_ui::confirm::Ask::PowerOff, "poweroff")] {
580580
let mut c = Canvas::new();
581581
settings::render(&mut c, &theme, &fonts, settings::ROW_RESTART, settings::max_scroll_px(),
582-
&settings::SettingsView { volume_limit: false, night: false, viz_name: "BARS · VEIL",
582+
&settings::SettingsView { ignore_the: false, volume_limit: false, night: false, viz_name: "BARS · VEIL",
583583
usb_dac: false, battery_care: true, device: "99% · 34.4 °C",
584584
database: "3,424 tracks", storage: "12.4 / 58 GB", sleep: "30 MIN",
585585
brightness: "4 / 5", screen_off: "OFF", auto_off: "OFF", boot_stock: "SONY", clock: "17 Aug · 09:01",
@@ -594,7 +594,7 @@ fn render_all(out: &mut dyn FnMut(&str, &Canvas), opts: &Opts) {
594594
let mut c = Canvas::new();
595595
settings::render(&mut c, &theme, &fonts, settings::ROW_BRIGHTNESS,
596596
settings::max_scroll_px() / 2,
597-
&settings::SettingsView { volume_limit: false, night: false, viz_name: "BARS · VEIL",
597+
&settings::SettingsView { ignore_the: false, volume_limit: false, night: false, viz_name: "BARS · VEIL",
598598
usb_dac: false, battery_care: true, device: "99% · 34.4 °C",
599599
database: "3,424 tracks", storage: "12.4 / 58 GB", sleep: "30 MIN",
600600
brightness: "4 / 5", screen_off: "OFF", auto_off: "OFF", boot_stock: "SONY", clock: "17 Aug · 09:01",
@@ -767,7 +767,7 @@ fn render_all(out: &mut dyn FnMut(&str, &Canvas), opts: &Opts) {
767767

768768
let mut c = Canvas::new();
769769
settings::render(&mut c, &theme, &fonts, settings::ROW_ACCENT, 0,
770-
&settings::SettingsView { volume_limit: false, night: false, viz_name: "BARS · VEIL", usb_dac: false,
770+
&settings::SettingsView { ignore_the: false, volume_limit: false, night: false, viz_name: "BARS · VEIL", usb_dac: false,
771771
battery_care: true, device: "99% · 34.4 °C", database: "3,424 tracks", storage: "12.4 / 58 GB", sleep: "30 MIN", brightness: "4 / 5",
772772
screen_off: "OFF", auto_off: "OFF", boot_stock: "SONY", clock: "17 Aug · 09:01", accent: a,
773773
palette: pal_name, accent_locked: pal_locked });

player/cinder-sim/src/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -388,7 +388,7 @@ fn render(app: &App, c: &mut Canvas, theme: &Theme, fonts: &FontSet) {
388388
source_direct: false, tone_control: false,
389389
}, 0, 0),
390390
Screen::Settings => settings::render(c, theme, fonts, 0, 0,
391-
&settings::SettingsView { volume_limit: false, night: app.night, viz_name: "BARS · VEIL", usb_dac: app.usb_dac, battery_care: false, device: "78% · 34.4 °C", database: "3,424 tracks", storage: "12.4 / 58 GB", sleep: "OFF", brightness: "4 / 5", screen_off: "OFF", auto_off: "OFF", boot_stock: "SONY", clock: "17 Aug · 09:01", accent: app.accent, palette: "Cinder", accent_locked: false }),
391+
&settings::SettingsView { ignore_the: false, volume_limit: false, night: app.night, viz_name: "BARS · VEIL", usb_dac: app.usb_dac, battery_care: false, device: "78% · 34.4 °C", database: "3,424 tracks", storage: "12.4 / 58 GB", sleep: "OFF", brightness: "4 / 5", screen_off: "OFF", auto_off: "OFF", boot_stock: "SONY", clock: "17 Aug · 09:01", accent: app.accent, palette: "Cinder", accent_locked: false }),
392392
Screen::Bluetooth => bluetooth::render(c, theme, fonts, &Bt {
393393
on: app.bt_on,
394394
connected: app.bt_conn.map(|r| PAIRED[r].name),

0 commit comments

Comments
 (0)