[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.

Cleanup

Kasper (Apr 21, 2024, 8:13 AM +0200) ec155754 a4f9455e

+52 -63
+1 -1
ferrum-addon/addon.d.ts
··· 177 177 } 178 178 export function get_image(index: number): JsImage | null 179 179 export function set_image(index: number, pathStr: string): void 180 - export function set_image_data(index: number, bytes: JsArrayBuffer, mimeType: string): void 180 + export function set_image_data(index: number, bytes: ArrayBuffer, mimeType: string): void 181 181 export function remove_image(index: number): void 182 182 export function update_track_info(trackId: string, info: TrackMd): void 183 183 export class ItunesImport {
+3 -3
src-native/data.rs
··· 50 50 println!("Write: {}ms", now.elapsed().as_millis()); 51 51 Ok(()) 52 52 } 53 - pub fn get_page_tracks<'a>(&'a self) -> &'a Vec<String> { 53 + pub fn get_page_tracks(&self) -> &Vec<String> { 54 54 match &self.page_track_ids { 55 - Some(ids) => &ids, 55 + Some(ids) => ids, 56 56 None => &self.open_playlist_track_ids, 57 57 } 58 58 } ··· 106 106 } 107 107 pub fn open_playlist(&mut self, playlist_id: TrackID) -> Result<()> { 108 108 self.open_playlist_id = playlist_id; 109 - self.open_playlist_track_ids = get_track_ids(&self)?; 109 + self.open_playlist_track_ids = get_track_ids(self)?; 110 110 self.page_track_ids = None; 111 111 match self.library.get_tracklist(&self.open_playlist_id)? { 112 112 TrackList::Special(_) => {
+1 -1
src-native/data_js.rs
··· 1 1 use crate::data::Data; 2 2 use napi::{Env, JsUndefined, Result}; 3 3 4 - pub fn get_data<'a>(env: &'a Env) -> Result<&'a mut Data> { 4 + pub fn get_data(env: &Env) -> Result<&mut Data> { 5 5 let data = env.get_instance_data::<Data>()?.ok_or(nerr!("No data"))?; 6 6 return Ok(data); 7 7 }
+8 -8
src-native/filter.rs
··· 75 75 .with_min_len(2000) 76 76 .filter(|id| { 77 77 let track = tracks.get(*id).expect("Track ID not found"); 78 - let is_match = find_match(&track.name, &keyword) 79 - || find_match(&track.artist, &keyword) 80 - || find_match_opt(&track.albumName, &keyword) 81 - || find_match_opt(&track.comments, &keyword) 82 - || find_match_opt(&track.genre, &keyword); 78 + let is_match = find_match(&track.name, keyword) 79 + || find_match(&track.artist, keyword) 80 + || find_match_opt(&track.albumName, keyword) 81 + || find_match_opt(&track.comments, keyword) 82 + || find_match_opt(&track.genre, keyword); 83 83 is_match 84 84 }) 85 85 .map(|id| id.clone()) ··· 93 93 None 94 94 } else { 95 95 let query: String = query.nfc().collect(); 96 - let mut keywords_iter = query.split(" "); 96 + let mut keywords_iter = query.split(' '); 97 97 let mut filtered_tracks = match keywords_iter.next() { 98 - Some(keyword) => filter_keyword(&data, &data.open_playlist_track_ids, keyword), 98 + Some(keyword) => filter_keyword(data, &data.open_playlist_track_ids, keyword), 99 99 None => return, 100 100 }; 101 101 for keyword in keywords_iter { 102 - filtered_tracks = filter_keyword(&data, &filtered_tracks, keyword); 102 + filtered_tracks = filter_keyword(data, &filtered_tracks, keyword); 103 103 } 104 104 Some(filtered_tracks) 105 105 };
+1 -1
src-native/itunes_import.rs
··· 273 273 datetime.unix_timestamp() * 1000 + datetime.millisecond() as i64 274 274 } 275 275 fn keep_true(value: Option<bool>) -> Option<bool> { 276 - value.filter(|v| *v == true) 276 + value.filter(|v| *v) 277 277 } 278 278 fn keep_filled(value: Option<String>) -> Option<String> { 279 279 value.filter(|v| v != "")
-3
src-native/lib.rs
··· 1 - #![allow(clippy::all)] 2 - #![deny(clippy::perf)] 3 - 4 1 use serde::de::DeserializeOwned; 5 2 use std::fs::File; 6 3 use std::io::BufReader;
+2 -2
src-native/page.rs
··· 50 50 pub fn refresh(env: Env) -> Result<JsUndefined> { 51 51 let data: &mut Data = get_data(&env)?; 52 52 let sort_key = data.sort_key.clone(); 53 - let sort_desc = data.sort_desc.clone(); 53 + let sort_desc = data.sort_desc; 54 54 sort(data, &sort_key, sort_desc)?; 55 55 filter::filter(data, data.filter.clone()); 56 56 return env.get_undefined(); ··· 173 173 TrackList::Folder(_) => return Err(nerr!("Cannot rearrange tracks in folder")), 174 174 TrackList::Special(_) => return Err(nerr!("Cannot rearrange tracks in special playlist")), 175 175 }; 176 - if data.sort_key != "index" || data.sort_desc != true { 176 + if data.sort_key != "index" || !data.sort_desc { 177 177 return Err(nerr!("Cannot rearrange when custom sorting is used")); 178 178 } 179 179 if data.filter != "" {
+3 -3
src-native/playlists.rs
··· 67 67 id: &String, 68 68 ids: &mut HashSet<String>, 69 69 ) -> Result<()> { 70 - let folder_children = match library.trackLists.get(&*id) { 70 + let folder_children = match library.trackLists.get(id) { 71 71 Some(TrackList::Playlist(_)) => return Ok(()), 72 72 Some(TrackList::Folder(folder)) => folder.children.clone(), 73 73 Some(TrackList::Special(_)) => throw!("Cannot delete special track list"), ··· 165 165 #[allow(dead_code)] 166 166 pub fn filter_duplicates(playlist_id: String, ids: Vec<String>, env: Env) -> Result<Vec<TrackID>> { 167 167 let data: &mut Data = get_data(&env)?; 168 - let mut track_ids: HashSet<String> = HashSet::from_iter(ids.into_iter()); 168 + let mut track_ids: HashSet<String> = HashSet::from_iter(ids); 169 169 let playlist = match data.library.get_tracklist_mut(&playlist_id)? { 170 170 TrackList::Playlist(playlist) => playlist, 171 171 _ => throw!("Cannot check if folder/special contains track"), ··· 190 190 TrackList::Folder(_) => throw!("Cannot remove track from folder"), 191 191 TrackList::Special(_) => throw!("Cannot remove track from special playlist"), 192 192 }; 193 - if data.sort_key != "index" || data.sort_desc != true { 193 + if data.sort_key != "index" || !data.sort_desc { 194 194 throw!("Cannot remove track when custom sorting is used"); 195 195 } 196 196 if data.filter != "" {
+14 -15
src-native/tracks/import.rs
··· 48 48 } 49 49 pub fn import_auto(data: &Data, path: &Path, now: i64) -> UniResult<Track> { 50 50 let track = match FileType::from_path(path)? { 51 - FileType::Mp3 => import_mp3(&data, &path, now)?, 52 - FileType::M4a => import_m4a(&data, &path, now)?, 53 - FileType::Opus => import_opus(&data, &path, now)?, 51 + FileType::Mp3 => import_mp3(data, path, now)?, 52 + FileType::M4a => import_m4a(data, path, now)?, 53 + FileType::Opus => import_opus(data, path, now)?, 54 54 }; 55 55 Ok(track) 56 56 } ··· 99 99 } 100 100 101 101 pub fn import_opus(data: &Data, track_path: &Path, now: i64) -> UniResult<Track> { 102 - let file_md = read_file_metadata(&track_path)?; 102 + let file_md = read_file_metadata(track_path)?; 103 103 104 104 let mut date_modified = match file_md.modified() { 105 105 Ok(sys_time) => sys_time_to_timestamp(&sys_time), ··· 114 114 Ok(opusfile) => opusfile, 115 115 Err(e) => throw!("Unable to read opus tags: {}", e), 116 116 }; 117 - let opus_properties = opusfile.properties().clone(); 117 + let opus_properties = *opusfile.properties(); 118 118 119 119 let mut tag_changed = false; 120 120 let tag = opusfile.vorbis_comments_mut(); ··· 134 134 let artist = tag.artist().map(|s| s.into_owned()).unwrap_or_default(); 135 135 136 136 let tracks_dir = &data.paths.tracks_dir; 137 - let filename = generate_filename(&tracks_dir, &artist, &title, "opus"); 137 + let filename = generate_filename(tracks_dir, &artist, &title, "opus"); 138 138 let dest_path = tracks_dir.join(&filename); 139 139 140 140 match fs::copy(track_path, &dest_path) { ··· 208 208 } 209 209 210 210 pub fn import_m4a(data: &Data, track_path: &Path, now: i64) -> UniResult<Track> { 211 - let file_md = read_file_metadata(&track_path)?; 211 + let file_md = read_file_metadata(track_path)?; 212 212 213 213 let mut date_modified = match file_md.modified() { 214 214 Ok(sys_time) => sys_time_to_timestamp(&sys_time), ··· 216 216 }; 217 217 218 218 let mut tag_changed = false; 219 - let mut tag = match mp4ameta::Tag::read_from_path(&track_path) { 219 + let mut tag = match mp4ameta::Tag::read_from_path(track_path) { 220 220 Ok(tag) => tag, 221 221 Err(e) => match e.kind { 222 222 mp4ameta::ErrorKind::NoTag => { ··· 265 265 let artist = tag.artist(); 266 266 267 267 let tracks_dir = &data.paths.tracks_dir; 268 - let filename = generate_filename(&tracks_dir, artist.unwrap_or(""), &title, "m4a"); 268 + let filename = generate_filename(tracks_dir, artist.unwrap_or(""), &title, "m4a"); 269 269 let dest_path = tracks_dir.join(&filename); 270 270 271 271 match fs::copy(track_path, &dest_path) { ··· 279 279 ); 280 280 281 281 if tag_changed { 282 - println!("Writing:::::::"); 283 282 match tag.write_to_path(&dest_path) { 284 283 Ok(_) => (), 285 284 Err(e) => throw!("Unable to tag file {}: {e}", dest_path.to_string_lossy()), ··· 339 338 fn get_first_text_id3(tag: &id3::Tag, id: &str) -> Option<String> { 340 339 let frame = tag.get(id)?; 341 340 let text = frame.content().text()?; 342 - return Some(text.to_owned()); 341 + Some(text.to_owned()) 343 342 } 344 343 pub fn import_mp3(data: &Data, track_path: &Path, now: i64) -> UniResult<Track> { 345 - let file_md = read_file_metadata(&track_path)?; 344 + let file_md = read_file_metadata(track_path)?; 346 345 347 346 let mut date_modified = match file_md.modified() { 348 347 Ok(sys_time) => sys_time_to_timestamp(&sys_time), 349 348 Err(_) => now, 350 349 }; 351 350 352 - let mp3_md = match mp3_metadata::read_from_file(&track_path) { 351 + let mp3_md = match mp3_metadata::read_from_file(track_path) { 353 352 Ok(md) => md, 354 353 Err(e) => throw!("Error reading mp3 metadata: {}", e), 355 354 }; ··· 369 368 }; 370 369 371 370 let mut tag_changed = false; 372 - let mut tag = match id3::Tag::read_from_path(&track_path) { 371 + let mut tag = match id3::Tag::read_from_path(track_path) { 373 372 Ok(tag) => tag, 374 373 Err(_) => id3::Tag::new(), 375 374 }; ··· 391 390 let artist = tag.artist(); 392 391 393 392 let tracks_dir = &data.paths.tracks_dir; 394 - let filename = generate_filename(&tracks_dir, artist.unwrap_or(""), &title, "mp3"); 393 + let filename = generate_filename(tracks_dir, artist.unwrap_or(""), &title, "mp3"); 395 394 let dest_path = tracks_dir.join(&filename); 396 395 397 396 match fs::copy(track_path, &dest_path) {
+2 -2
src-native/tracks/md.rs
··· 100 100 Err(_) => throw!("Invalid year"), 101 101 }, 102 102 }; 103 - let new_year_i64 = new_year_i32.map(|n| i64::from(n)); 103 + let new_year_i64 = new_year_i32.map(i64::from); 104 104 match new_year_i32 { 105 105 None => tag.remove_year(), 106 106 Some(value) => tag.set_year(value), ··· 174 174 175 175 // move file 176 176 if new_name != track.name || new_artist != track.artist { 177 - let new_filename = generate_filename(&tracks_dir, &new_artist, &new_name, &ext); 177 + let new_filename = generate_filename(tracks_dir, &new_artist, &new_name, &ext); 178 178 let new_path = tracks_dir.join(&new_filename); 179 179 match fs::rename(old_path, new_path) { 180 180 Ok(_) => {
+10 -10
src-native/tracks/mod.rs
··· 14 14 pub use tag::Tag; 15 15 16 16 fn id_to_track<'a>(env: &'a Env, id: &String) -> Result<&'a mut Track> { 17 - let data: &mut Data = get_data(&env)?; 17 + let data: &mut Data = get_data(env)?; 18 18 let tracks = &mut data.library.tracks; 19 19 let track = tracks.get_mut(id).ok_or(nerr("Track ID not found"))?; 20 20 return Ok(track); ··· 110 110 let track = id_to_track(&env, &track_id)?; 111 111 let tracks_dir = &data.paths.tracks_dir; 112 112 let file_path = tracks_dir.join(&track.file); 113 - let task = ReadCover(file_path.into(), index.into()); 113 + let task = ReadCover(file_path, index.into()); 114 114 env.spawn(task).map(|t| t.promise_object()) 115 115 } 116 116 #[napi( ··· 124 124 } 125 125 126 126 fn sanitize_filename(input: &String) -> String { 127 - let mut string = input.replace("/", "_"); 128 - string = string.replace("?", "_"); 129 - string = string.replace("<", "_"); 130 - string = string.replace(">", "_"); 131 - string = string.replace("\\", "_"); 132 - string = string.replace(":", "_"); 133 - string = string.replace("*", "_"); 134 - string = string.replace("\"", "_"); 127 + let mut string = input.replace('/', "_"); 128 + string = string.replace('?', "_"); 129 + string = string.replace('<', "_"); 130 + string = string.replace('>', "_"); 131 + string = string.replace('\\', "_"); 132 + string = string.replace(':', "_"); 133 + string = string.replace('*', "_"); 134 + string = string.replace('\"', "_"); 135 135 // prevent control characters: 136 136 string = string.replace("0x", "__"); 137 137 // Filenames can be max 255 bytes. We use 230 to give
+7 -14
src-native/tracks/tag.rs
··· 1 1 use crate::{UniError, UniResult}; 2 2 use id3::{self, TagLike}; 3 3 use lofty::{file::TaggedFileExt, tag::Accessor, tag::TagExt}; 4 - use mp4ameta; 5 4 use std::io::Cursor; 6 5 use std::path::{Path, PathBuf}; 7 6 ··· 29 28 } 30 29 31 30 pub fn id3_timestamp_from_year(year: i32) -> id3::Timestamp { 32 - return id3::Timestamp { 31 + id3::Timestamp { 33 32 year, 34 33 month: None, 35 34 day: None, 36 35 hour: None, 37 36 minute: None, 38 37 second: None, 39 - }; 38 + } 40 39 } 41 40 42 41 pub struct Image { ··· 68 67 69 68 let tag = match ext.as_ref() { 70 69 "mp3" => { 71 - let tag = match id3::Tag::read_from_path(&path) { 70 + let tag = match id3::Tag::read_from_path(path) { 72 71 Ok(tag) => tag, 73 72 Err(_) => id3::Tag::new(), 74 73 }; 75 74 Tag::Id3(tag) 76 75 } 77 76 "m4a" => { 78 - let tag = match mp4ameta::Tag::read_from_path(&path) { 77 + let tag = match mp4ameta::Tag::read_from_path(path) { 79 78 Ok(tag) => tag, 80 79 Err(e) => match e.kind { 81 80 mp4ameta::ErrorKind::NoTag => { ··· 101 100 102 101 let mut tagged_file = match probe.read() { 103 102 Ok(f) => f, 104 - Err(e) => { 105 - println!("1>>>>> {}", e); 106 - println!("2>>>>> {:#?}", e.kind()); 107 - println!("3>>>>> {}", e.to_string()); 108 - throw!("Unable to read file: {}", e) 109 - } 103 + Err(e) => throw!("Unable to read file: {}", e), 110 104 }; 111 105 112 106 let tag = match tagged_file.remove(lofty::tag::TagType::VorbisComments) { ··· 448 442 let mut pic_frames: Vec<_> = tag 449 443 .frames() 450 444 .filter(|frame| frame.content().picture().is_some()) 451 - .map(|frame| frame.clone()) 445 + .cloned() 452 446 .collect(); 453 447 let mut new_pic = id3::frame::Picture { 454 448 mime_type: match mime_type { ··· 512 506 Ok(picture) => picture, 513 507 Err(e) => throw!("Unable to read picture: {}", e), 514 508 }; 515 - println!("picture.mime_type() {:?}", picture.mime_type()); 516 509 match picture.mime_type() { 517 510 Some(lofty::picture::MimeType::Png | lofty::picture::MimeType::Jpeg) => { 518 511 tag.set_picture(index, picture); ··· 618 611 let mut pic_frames: Vec<_> = tag 619 612 .frames() 620 613 .filter(|frame| frame.content().picture().is_some()) 621 - .map(|frame| frame.clone()) 614 + .cloned() 622 615 .collect(); 623 616 pic_frames.remove(index); 624 617 tag.remove_all_pictures();