[READ-ONLY] Mirror of https://github.com/probablykasper/ferrum. Music library app for Mac, Linux and Windows ferrum.kasper.space
electron linux macos music music-library music-player napi windows
0

Configure Feed

Select the types of activity you want to include in your feed.

Fix panic from sorting

The panic was because we were intentionally not having a "total order" (for grouping album tracks). This was fine before, but in Rust 1.81 the sorting function was updated

Kasper (Oct 6, 2024, 7:15 AM +0200) ca7f8cd7 b57d22ec

+66 -29
+10
src-native/library_types.rs
··· 324 324 #[serde(default, skip_serializing_if = "Option::is_none")] 325 325 pub volume: Option<i8>, 326 326 } 327 + impl Track { 328 + pub fn has_album(&self) -> bool { 329 + self.albumName.is_some() && self.albumArtist.is_some() 330 + } 331 + pub fn is_same_album(&self, other: &Track) -> bool { 332 + self.has_album() 333 + && self.albumName == other.albumName 334 + && self.albumArtist == other.albumArtist 335 + } 336 + } 327 337 328 338 #[derive(Serialize, Deserialize, Clone, Debug)] 329 339 #[napi(object)]
+56 -29
src-native/sort.rs
··· 91 91 92 92 let id_map = TRACK_ID_MAP.read().unwrap(); 93 93 94 - let mut track_ids = get_tracklist_item_ids(library, &options.playlist_id)?; 94 + let mut item_ids = get_tracklist_item_ids(library, &options.playlist_id)?; 95 + let item_count = item_ids.len(); 95 96 96 97 if options.sort_key == "index" { 97 98 // Note: Indexes descend from "first to last", unlike 98 99 // other numbers which ascend from "high to low" 99 100 if !options.sort_desc { 100 - track_ids.reverse(); 101 + item_ids.reverse(); 101 102 } 102 103 println!("Sort: {}ms", now.elapsed().as_millis()); 103 - return Ok(track_ids); 104 + return Ok(item_ids); 104 105 } 105 106 106 107 let tracks = library.get_tracks(); 107 108 let field = get_track_field_type(&options.sort_key)?; 108 - let subsort_field = options.group_album_tracks 109 + let group_album_tracks = options.group_album_tracks 109 110 && match options.sort_key.as_str() { 110 111 "dateAdded" | "albumName" | "comments" | "genre" | "year" | "artist" => true, 111 112 _ => false, 112 113 }; 113 - track_ids.sort_by(|id_a, id_b| { 114 - let track_id_a = &id_map[*id_a as usize]; 115 - let track_id_b = &id_map[*id_b as usize]; 114 + item_ids.sort_by(|&id_a, &id_b| { 115 + let track_id_a = &id_map[id_a as usize]; 116 + let track_id_b = &id_map[id_b as usize]; 116 117 let track_a = tracks.get(track_id_a).expect("Track ID non-existant (1)"); 117 118 let track_b = tracks.get(track_id_b).expect("Track ID non-existant (2)"); 118 - if subsort_field 119 - && track_a.albumName.is_some() 120 - && track_a.albumName == track_b.albumName 121 - && track_a.albumArtist.is_some() 122 - && track_a.albumArtist == track_b.albumArtist 123 - { 124 - match compare_track_field(track_a, track_b, "discNum", &TrackField::U32) { 125 - Ordering::Equal => {} 126 - order => return order, 127 - }; 128 - match compare_track_field(track_a, track_b, "trackNum", &TrackField::U32) { 129 - Ordering::Equal => {} 130 - order => return order, 131 - }; 119 + compare_track_field(track_a, track_b, &options.sort_key, &field) 120 + }); 121 + 122 + if options.sort_desc { 123 + item_ids.reverse(); 124 + } 125 + 126 + if group_album_tracks { 127 + let mut grouped_track_ids: Vec<ItemId> = Vec::with_capacity(item_ids.len()); 128 + let mut item_ids_iter = item_ids.into_iter().peekable(); 129 + 130 + // Process the first track in the next album 131 + while let Some(first_item_id) = item_ids_iter.next() { 132 + // We need to get the first track to compare with the later tracks 133 + let first_track_id = &id_map[first_item_id as usize]; 134 + let first_track = tracks.get(first_track_id).unwrap(); 135 + let mut current_album_buffer: Vec<ItemId> = vec![first_item_id]; 136 + 137 + // Collect the rest of the tracks from the same album 138 + while let Some(item_id) = item_ids_iter.peek() { 139 + let track_id = &id_map[*item_id as usize]; 140 + let track = tracks.get(track_id).unwrap(); 141 + if !track.is_same_album(first_track) { 142 + break; 143 + } 144 + current_album_buffer.push(*item_id); 145 + item_ids_iter.next(); 146 + } 147 + 148 + // Sort album tracks by discNum, then trackNum 149 + current_album_buffer.sort_by(|&id_a, &id_b| { 150 + let track_a = tracks.get(&id_map[id_a as usize]).unwrap(); 151 + let track_b = tracks.get(&id_map[id_b as usize]).unwrap(); 152 + let mut order = compare_track_field(track_a, track_b, "discNum", &TrackField::U32); 153 + if order == Ordering::Equal { 154 + order = compare_track_field(track_a, track_b, "trackNum", &TrackField::U32); 155 + } 156 + order 157 + }); 158 + 159 + grouped_track_ids.append(&mut current_album_buffer); 132 160 } 133 - let order = compare_track_field(track_a, track_b, &options.sort_key, &field); 134 - return match options.sort_desc { 135 - true => order.reverse(), 136 - false => order, 137 - }; 138 - }); 139 - println!("Sort: {}ms", now.elapsed().as_millis()); 140 - return Ok(track_ids); 161 + 162 + assert_eq!(item_count, grouped_track_ids.len()); 163 + item_ids = grouped_track_ids; 164 + } 165 + 166 + println!("Sort group: {}ms", now.elapsed().as_millis()); 167 + return Ok(item_ids); 141 168 } 142 169 143 170 pub fn compare_track_field(a: &Track, b: &Track, sort_key: &str, field: &TrackField) -> Ordering {