diff --git a/Cargo.lock b/Cargo.lock index c97727b..797e5a2 100755 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,15 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" +[[package]] +name = "aho-corasick" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba" +dependencies = [ + "memchr", +] + [[package]] name = "anstream" version = "1.0.0" @@ -191,6 +200,12 @@ version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" +[[package]] +name = "memchr" +version = "2.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" + [[package]] name = "miniz_oxide" version = "0.8.9" @@ -208,6 +223,7 @@ dependencies = [ "clap", "glob", "lofty", + "regex", ] [[package]] @@ -249,6 +265,35 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "regex" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" + [[package]] name = "simd-adler32" version = "0.3.10" diff --git a/Cargo.toml b/Cargo.toml index 198cc00..9028af1 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,3 +7,4 @@ edition = "2024" clap = { version = "4.6.6", features = ["derive"] } glob = "0.3.4" lofty = "0.25.1" +regex = "1.13.1" diff --git a/src/edit.rs b/src/edit.rs index fb86712..8789359 100644 --- a/src/edit.rs +++ b/src/edit.rs @@ -1,25 +1,30 @@ use lofty::config::WriteOptions; +use lofty::tag::TagItem; use lofty::tag::TagType::VorbisComments; -use lofty::tag::{Accessor, TagItem}; use lofty::tag::{ItemKey, ItemValue}; use lofty::tag::{Tag, TagExt}; +use regex::Regex; use std::collections::HashMap; use std::env::var; use std::io::Write; use std::path::PathBuf; -use std::{fs::File, fs::read_dir, io::Read, process::Command}; +use std::{fs::read_dir, fs::File, io::Read, process::Command}; pub fn edit(file_path: PathBuf, extra_files: [String; 8]) -> std::io::Result<()> { - let tmp_file = PathBuf::from("/tmp/keks-sort-metadata"); + let tmp_file_path = PathBuf::from("/tmp/keks-sort-metadata"); + let mut tmp_file = File::create(&tmp_file_path)?; let editor = var("EDITOR").unwrap(); - let mut file = File::create(&tmp_file)?; - let mut tags: Vec = Vec::new(); let mut path: PathBuf = file_path.clone(); + let mut files = Vec::new(); + + struct FileMetadata { + path: PathBuf, + tag: Tag, + } if file_path.is_dir() { for music_file in read_dir(&file_path)? { path = music_file?.path(); - println!("{:?}", path); if path .extension() @@ -30,57 +35,82 @@ pub fn edit(file_path: PathBuf, extra_files: [String; 8]) -> std::io::Result<()> } let tag = crate::get_metadata(&path); - tags.push(tag); + files.push(FileMetadata { path, tag }); } } else { - let tag = &crate::get_metadata(&file_path); - tags.push(tag.clone()); + let tag = &crate::get_metadata(file_path); + files.push(FileMetadata { + path, + tag: tag.clone(), + }); } - for tag in &tags { - println!("{:?}", tag.title()); - writeln!(file.try_clone()?, "------------ {:?} ------------", path)?; - for item in tag.items() { + for file in &files { + writeln!( + tmp_file.try_clone()?, + "------------ {:?} ------------", + file.path + )?; + for item in file.tag.items() { writeln!( - file, + tmp_file, "{} = {}", item.key().map_key(VorbisComments).unwrap(), item.value().text().unwrap() )?; } - writeln!(file.try_clone()?)?; // writes empty line + writeln!(tmp_file.try_clone()?)?; // writes empty line } Command::new(editor) - .arg(&tmp_file) + .arg(&tmp_file_path) .status() .expect("Something went wrong"); let mut editable = String::new(); - File::open(&tmp_file) + File::open(&tmp_file_path) .expect("Could not open file") .read_to_string(&mut editable)?; let parsed = parse(&editable); - for mut tag in tags { - for (key, value) in &parsed { + for (mut file, parse) in files.into_iter().zip(parsed) { + for (key, value) in &parse { let item_key = ItemKey::from_key(VorbisComments, key).expect("ERROR: Failed to get item key"); let item = TagItem::new(item_key, ItemValue::Text(value.to_string())); - tag.insert(item.clone()); + file.tag.insert(item); } - tag.save_to_path(&file_path, WriteOptions::default()) + file.tag + .save_to_path(&file.path, WriteOptions::default()) .expect("ERROR: Failed to write tag"); } Ok(()) } -fn parse(input: &str) -> HashMap { - input - .lines() - .filter_map(|line| { - let (key, value) = line.split_once('=')?; - Some((key.trim().to_string(), value.trim().to_string())) +fn parse(input: &str) -> Vec> { + let re = Regex::new(r#"(?m)^------------ ".*?" ------------\r?\n"#).unwrap(); + let parts: Vec<&str> = re + .split(input) + .filter(|part| !part.trim().is_empty()) + .collect(); + let result: Vec> = parts + .into_iter() + .filter_map(|part| { + let part = part.trim(); + + if part.is_empty() { + return None; + } + + Some( + part.lines() + .filter_map(|line| { + let (key, value) = line.split_once('=')?; + Some((key.trim().to_string(), value.trim().to_string())) + }) + .collect(), + ) }) - .collect() + .collect(); + result } diff --git a/src/move_files.rs b/src/move_files.rs index 40106b8..3722b42 100644 --- a/src/move_files.rs +++ b/src/move_files.rs @@ -12,7 +12,7 @@ pub fn move_files( should_copy: bool, has_parent: bool, ) -> Result<(), Box> { - let target_dir = PathBuf::from("/home/user/Music/Tracks"); + let target_dir = PathBuf::from("/home/user/Desktop/coding/keks-sort"); let artist = tag.artist(); let album = tag.album(); @@ -37,9 +37,10 @@ pub fn move_files( let target = target_dir.join(artist_dir).join(album_dir.as_ref()); let file_name = if let Some(track_number) = track_number { + let number: u32 = track_number.parse()?; PathBuf::from(format!( "{:02}. {} - {}.flac", - track_number, + number, artist.unwrap(), title.unwrap(), ))