From bc067e2739b839bf2ee906ec4ba7d08ee1ad9415 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Sun, 16 Mar 2025 08:50:29 +0200 Subject: [PATCH] load metadata using lofty (shout out @Frieder_Hannenheim - you are #1) --- Cargo.lock | 42 +++++++++++++++++++++++++++++ Cargo.toml | 21 ++++++++------- src/main.rs | 8 +++--- src/model.rs | 74 +++++++++++++++++++++++++--------------------------- 4 files changed, 92 insertions(+), 53 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 08a3b2c..2a9d7bf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -324,6 +324,12 @@ dependencies = [ "syn", ] +[[package]] +name = "data-encoding" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "575f75dfd25738df5b91b8e43e14d44bda14637a58fae779fd2b064f8bf3e010" + [[package]] name = "dbus" version = "0.9.7" @@ -571,6 +577,32 @@ dependencies = [ "scopeguard", ] +[[package]] +name = "lofty" +version = "0.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "781de624f162b1a8cbfbd577103ee9b8e5f62854b053ff48f4e31e68a0a7df6f" +dependencies = [ + "byteorder", + "data-encoding", + "flate2", + "lofty_attr", + "log", + "ogg_pager", + "paste", +] + +[[package]] +name = "lofty_attr" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed9983e64b2358522f745c1251924e3ab7252d55637e80f6a0a3de642d6a9efc" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "log" version = "0.4.26" @@ -640,6 +672,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "ogg_pager" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e034c10fb5c1c012c1b327b85df89fb0ef98ae66ec28af30f0d1eed804a40c19" +dependencies = [ + "byteorder", +] + [[package]] name = "once_cell" version = "1.21.1" @@ -1060,6 +1101,7 @@ dependencies = [ "file_type", "hex", "id3", + "lofty", "opener", "opus_headers", "pad", diff --git a/Cargo.toml b/Cargo.toml index f54d164..03438d2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,18 +5,19 @@ edition = "2024" [dependencies] tengri = { git = "https://codeberg.org/unspeaker/tengri", rev = "877b344765" } -enolib = { git = "https://codeberg.org/simonrepp/enolib-rs", tag = "0.5.0" } -clap = { version = "4.5.4", features = [ "cargo" ] } -file_type = "0.7" -hex = "0.4" -id3 = "1.16" -opener = "0.7" -opus_headers = "0.1.2" -pad = "0.1" +clap = { version = "4.5.4", features = [ "cargo" ] } +enolib = { git = "https://codeberg.org/simonrepp/enolib-rs", tag = "0.5.0" } +file_type = "0.7" +hex = "0.4" +id3 = "1.16" +lofty = "0.22.2" +opener = "0.7" +opus_headers = "0.1.2" +pad = "0.1" unicode-width = "0.2" -walkdir = "2" -xxhash-rust = { version = "0.8.5", features = ["xxh3"] } +walkdir = "2" +xxhash-rust = { version = "0.8.5", features = ["xxh3"] } #base64 = "0.22" #moku = "0.2" diff --git a/src/main.rs b/src/main.rs index 22a5888..245296a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,7 +4,6 @@ use std::sync::{Arc, RwLock}; use std::path::{Path, PathBuf}; use std::env::{current_dir, set_current_dir}; -use std::fs::read; use std::thread::{sleep, spawn, JoinHandle}; use std::time::Duration; @@ -12,7 +11,7 @@ use tengri::{input::*, output::*, tui::*}; use crate::crossterm::event::{Event, KeyEvent, KeyCode, KeyModifiers, KeyEventState, KeyEventKind}; use clap::{arg, command, value_parser}; -use walkdir::{WalkDir, DirEntry}; +use walkdir::WalkDir; use xxhash_rust::xxh3::xxh3_64; use file_type::FileType; @@ -55,11 +54,10 @@ fn main () -> Usually<()> { if let Some(true) = args.get_one::("check") { return Ok(()) } - return Tui::new()?.run(&state) + Tui::new()?.run(&state) } else { panic!("read did not finish") } - Ok(()) } fn collect (root: &impl AsRef, thread_count: usize) -> Usually>>> { @@ -96,7 +94,7 @@ fn collect (root: &impl AsRef, thread_count: usize) -> Usually, title: Option>, date: Option>, - year: Option, + year: Option, people: Option>>, publisher: Option>, key: Option>, @@ -104,7 +109,7 @@ impl Entry { Ok(None) } } - fn new_dir (root: &impl AsRef, path: &Path, depth: usize) -> Perhaps { + fn new_dir (_: &impl AsRef, path: &Path, depth: usize) -> Perhaps { Ok(Some(Self { depth, path: path.into(), @@ -116,11 +121,11 @@ impl Entry { }, })) } - fn new_file (root: &impl AsRef, path: &Path, depth: usize) -> Perhaps { + fn new_file (_: &impl AsRef, path: &Path, depth: usize) -> Perhaps { Ok(Some(Self { depth, + info: EntryInfo::new(path)?, path: path.into(), - info: EntryInfo::new(&read(path)?)? })) } pub fn is_dir (&self) -> bool { @@ -165,19 +170,23 @@ impl Entry { } } impl EntryInfo { - pub fn new (bytes: &[u8]) -> Usually { - // MP3 with ID3v2 - if bytes.starts_with(&[b'I', b'D', b'3']) { - #[allow(deprecated)] - let id3 = Tag::read_from(bytes)?; - return Ok(Self::Music { - hash: hex::encode(xxh3_64(&bytes).to_be_bytes()).into(), + pub fn new (path: &Path) -> Usually { + let reader = BufReader::new(File::open(path)?); + let probe = Probe::new(reader) + //.options(ParseOptions::new().parsing_mode(ParsingMode::Strict)) + .guess_file_type()?; + if probe.file_type().is_some() { + let file = lofty::read_from_path(path)?; + let tag = file.primary_tag(); + let hash = hex::encode(xxh3_64(std::fs::read(path)?.as_slice()).to_be_bytes()).into(); + Ok(Self::Music { + hash, file_type: None, - artist: id3.artist().map(|x|x.into()), - album: id3.album().map(|x|x.into()), - track: id3.track().map(|x|x.into()), - title: id3.title().map(|x|x.into()), - year: id3.year().map(|x|x.into()), + artist: tag.map(|t|t.artist().map(|t|t.into())).flatten(), + album: tag.map(|t|t.album().map(|t|t.into())).flatten(), + track: tag.map(|t|t.track().map(|t|t.into())).flatten(), + title: tag.map(|t|t.title().map(|t|t.into())).flatten(), + year: tag.map(|t|t.year().map(|t|t.into())).flatten(), date: None, people: None, publisher: None, @@ -185,29 +194,18 @@ impl EntryInfo { bpm: None, invalid: false, }) + } else { + Self::new_fallback(path) } - // Ogg (Opus) - if bytes.starts_with(&[b'O', b'g', b'g', b'S']) { - let headers = opus_headers::parse_from_read(bytes)?; - println!("{headers:?}"); - return Ok(Self::Music { - hash: hex::encode(xxh3_64(&bytes).to_be_bytes()).into(), - file_type: None, - artist: None, - album: None, - track: None, - title: None, - date: None, - year: None, - people: None, - publisher: None, - key: None, - bpm: None, - invalid: false, - }) - } + } + pub fn new_fallback (path: &Path) -> Usually { + let mut reader = BufReader::new(File::open(path)?); + let mut bytes = vec![0;16]; + reader.read(&mut bytes)?; // PNG if bytes.starts_with(&[0x89, b'P', b'N', b'G', 0x0D, 0x0A, 0x1A, 0x0A]) { + let mut bytes = vec![]; + BufReader::new(File::open(path)?).read(&mut bytes)?; return Ok(Self::Image { file_type: None, hash: hex::encode(xxh3_64(&bytes).to_be_bytes()).into(),