From fcf044db0618c1901bc3c46b238353c95491cbfc Mon Sep 17 00:00:00 2001 From: Jakub Trzeciak Date: Tue, 12 Sep 2023 09:24:19 +0200 Subject: Init --- .gitignore | 1 + Cargo.lock | 184 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 10 ++++ default.nix | 18 ++++++ src/main.rs | 169 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 382 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 default.nix create mode 100644 src/main.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..9c38170 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,184 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aho-corasick" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" +dependencies = [ + "memchr", +] + +[[package]] +name = "base64" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "flate2" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "lofty" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cb2b21027aa98f860de475b3ee8b10f36df689121b5752efeb7a64bec1f4d45" +dependencies = [ + "base64", + "byteorder", + "flate2", + "lofty_attr", + "log", + "ogg_pager", + "once_cell", + "paste", +] + +[[package]] +name = "lofty_attr" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9482ad911db377f8362c658aff576aac289c84cf5c25ca058e2c49d9bc3301dc" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "log" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + +[[package]] +name = "ogg-tagger" +version = "0.1.0" +dependencies = [ + "lofty", + "regex", +] + +[[package]] +name = "ogg_pager" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d218a406e5de88e1c492d0162d569916f7436efe851ba5cc40a4bf4fa97cb40" +dependencies = [ + "byteorder", +] + +[[package]] +name = "once_cell" +version = "1.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" + +[[package]] +name = "paste" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" + +[[package]] +name = "proc-macro2" +version = "1.0.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6aeca18b86b413c660b781aa319e4e2648a3e6f9eadc9b47e9038e6fe9f3451b" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81ca098a9821bd52d6b24fd8b10bd081f47d39c22778cafaa75a2857a62c6390" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" + +[[package]] +name = "syn" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..6139a41 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "ogg-tagger" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +lofty = "0.13.0" +regex = "1.8.3" \ No newline at end of file diff --git a/default.nix b/default.nix new file mode 100644 index 0000000..c7aacee --- /dev/null +++ b/default.nix @@ -0,0 +1,18 @@ +{ pkgs ? import { } }: + +let + manifest = (pkgs.lib.importTOML ./Cargo.toml).package; +in pkgs.rustPlatform.buildRustPackage rec { + pname = manifest.name; + version = manifest.version; + + cargoLock.lockFile = ./Cargo.lock; + src = pkgs.lib.cleanSource ./.; + + meta = with pkgs.lib; { + description = "A fast line-oriented regex search tool, similar to ag and ack"; + homepage = "https://github.com/BurntSushi/ripgrep"; + license = licenses.gpl3; + maintainers = [ "jptrzy" ]; + }; +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..2bf1274 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,169 @@ +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()); + } + } +} \ No newline at end of file -- cgit v1.2.3