use std::{fs::{self, File}, path::Path, io::{Error, BufReader, Read}, fmt::format, os::fd::IntoRawFd}; use lofty::{Probe, TaggedFileExt, LoftyError, TagExt, Tag, Picture, Accessor, PictureType}; /* Title Artist - Title Artist - Album - Title Artist - Album - Nr - Title Artist - Album - Nr - Max Nr - Title */ struct InnerTag { title: String, artist: Option, album: Option, track: Option, } impl InnerTag { pub fn empty() -> InnerTag { InnerTag {title: "".into(), artist: None, album: None, track: None} } pub fn from(name: &str) -> Result{ let mut inner_tag: InnerTag = InnerTag::empty(); let mut tags: Vec = Vec::new(); { // TODO make it faster and cleaner let mut text: String = "".into(); let mut connected = false; for s in name.split("-") { if text == "" { text = s.into(); } else { if s == "" { connected = true; text += "-"; } else if connected { text += s; connected = false; } else { tags.push(text); text = s.into(); } } } tags.push(text); } match tags.len() { 1 => { inner_tag.title = tags.remove(0); }, 2 => { inner_tag.artist = Some(tags.remove(0)); inner_tag.title = tags.remove(0); }, 3 => { inner_tag.artist = Some(tags.remove(0)); inner_tag.album = Some(tags.remove(0)); inner_tag.title = tags.remove(0); }, 4 => { inner_tag.artist = Some(tags.remove(0)); inner_tag.album = Some(tags.remove(0)); inner_tag.track = Some(tags.remove(0).parse().unwrap()); inner_tag.title = tags.remove(0); }, 5 => { inner_tag.artist = Some(tags.remove(0)); inner_tag.album = Some(tags.remove(0)); inner_tag.track = Some(tags.remove(0).parse().unwrap()); inner_tag.title = tags.remove(1); }, _ => panic!("FUCK to many tags {}", tags.len()) } return Ok(inner_tag); } } fn truncate(s: &str, min_chars: usize, max_chars: usize) -> &str { let e = match s.char_indices().nth(min_chars) { None => s, Some((idx, _)) => &s[idx..], }; if max_chars < min_chars { return ""; } return match e.char_indices().nth(max_chars - min_chars) { None => e, Some((idx, _)) => &e[..idx], } } fn tag_ogg_file(path: &Path) -> Result<(), LoftyError> { let mut tagged_file = Probe::open(path) .expect("ERROR: Bad path provided!") .read()?; let tag = match tagged_file.primary_tag_mut() { Some(primary_tag) => primary_tag, None => { if let Some(first_tag) = tagged_file.first_tag_mut() { first_tag } else { let tag_type = tagged_file.primary_tag_type(); eprintln!("WARN: No tags found, creating a new tag of type `{tag_type:?}`"); tagged_file.insert_tag(Tag::new(tag_type)); tagged_file.primary_tag_mut().unwrap() } }, }; let path_without_ext = path.with_extension(""); let name = path_without_ext.file_name().unwrap().to_str().unwrap(); let inner_tag = InnerTag::from(name).unwrap(); tag.clear(); tag.set_title(inner_tag.title); tag.set_artist(inner_tag.artist.unwrap_or("".into())); tag.set_album(inner_tag.album.unwrap_or("".into())); if let Some(track) = inner_tag.track { tag.set_track(track); } if let Some(album) = tag.album() { if album != "" { let image_path = path.parent().unwrap().join(format!(".cover/{}/{}.jpg", tag.artist().unwrap(), album)); if image_path.exists() { let image_file = &mut File::open(image_path.clone()).unwrap(); let mut picture = Picture::from_reader(image_file).unwrap(); picture.set_pic_type(PictureType::CoverFront); tag.set_picture(0, picture) } else { println!("⚠️ Can't found image {}", image_path.to_str().unwrap()); } } } tag.save_to_path(path).expect("ERROR: Can't save ogg file"); println!("✅ {}", name); return Ok(()); } fn main() { let music_dir = fs::read_dir("/home/jp3/Music").unwrap(); for path in music_dir { // println!("Name: {}", path.unwrap().file_name().to_str().unwrap()) if path.as_ref().unwrap().file_type().unwrap().is_file() { tag_ogg_file(path.unwrap().path().as_path()); } } }