From 928d38bfaaf5080c585f66d007231ae11e575447 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Tue, 11 Mar 2025 14:59:11 +0200 Subject: [PATCH] identify files manually by magic numbers --- Cargo.lock | 13 ++++ Cargo.toml | 22 +++--- src/main.rs | 6 +- src/model.rs | 210 ++++++++++++++++++++++++++++++--------------------- src/view.rs | 2 +- 5 files changed, 154 insertions(+), 99 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 65d59ad..c286128 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -347,6 +347,11 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" +[[package]] +name = "enolib" +version = "0.5.0" +source = "git+https://codeberg.org/simonrepp/enolib-rs?tag=0.5.0#011a93092e5127c9354d643e9ac51ff592cf59f2" + [[package]] name = "equivalent" version = "1.0.2" @@ -662,6 +667,12 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "opus_headers" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afbb993947f111397c2bc536944f8dac7f54a4e73383d478efe1990b56404b60" + [[package]] name = "pad" version = "0.1.6" @@ -1054,10 +1065,12 @@ name = "taggart" version = "0.1.0" dependencies = [ "clap", + "enolib", "file_type", "hex", "id3", "opener", + "opus_headers", "pad", "tek_tui", "unicode-width 0.2.0", diff --git a/Cargo.toml b/Cargo.toml index 31c8f25..ba29f09 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,16 +5,18 @@ edition = "2024" [dependencies] tek_tui = { git = "https://codeberg.org/unspeaker/tengri", rev = "6cd85ef" } +enolib = { git = "https://codeberg.org/simonrepp/enolib-rs", tag = "0.5.0" } -clap = { version = "4.5.4", features = [ "cargo" ] } -walkdir = "2" -id3 = "1.16" -#moku = "0.2" -file_type = "0.7" -pad = "0.1" -#sha2 = "0.10" -hex = "0.4" -xxhash-rust = { version = "0.8.5", features = ["xxh3"] } #base64 = "0.22" -unicode-width = "0.2" +#moku = "0.2" +#sha2 = "0.10" +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" +unicode-width = "0.2" +walkdir = "2" +xxhash-rust = { version = "0.8.5", features = ["xxh3"] } diff --git a/src/main.rs b/src/main.rs index 7f9f4d0..6b95aac 100644 --- a/src/main.rs +++ b/src/main.rs @@ -27,7 +27,11 @@ pub(crate) const PAGE_SIZE: usize = 10; fn cli () -> clap::Command { command!() - .arg(arg!([path] "Path to root directory").value_parser(value_parser!(PathBuf))) + .arg(arg!([path] "Path to root directory") + .value_parser(value_parser!(PathBuf))) + //.arg(arg!(-j --threads "Number of indexing threads") + //.required(false) + //.value_parser(value_parser!(usize))) } fn main () -> Usually<()> { diff --git a/src/model.rs b/src/model.rs index c6fd0b5..6b84b56 100644 --- a/src/model.rs +++ b/src/model.rs @@ -13,6 +13,49 @@ pub struct Taggart { pub editing: Option<(usize, String)>, } +#[derive(Ord, Eq, PartialEq, PartialOrd)] +pub struct Entry { + pub path: PathBuf, + pub depth: usize, + pub info: EntryInfo, +} + +#[derive(Ord, Eq, PartialEq, PartialOrd)] +pub enum EntryInfo { + Directory { + hash_file: Option<()>, + catalog_file: Option<()>, + artist_file: Option<()>, + release_file: Option<()>, + }, + Music { + hash: Arc, + file_type: Option<&'static FileType>, + artist: Option>, + album: Option>, + track: Option, + title: Option>, + date: Option>, + year: Option, + people: Option>>, + publisher: Option>, + key: Option>, + bpm: Option>, + invalid: bool, + }, + Image { + hash: Arc, + file_type: Option<&'static FileType>, + title: Option, + author: Option, + invalid: bool, + }, + Unknown { + hash: Arc, + file_type: Option<&'static FileType>, + } +} + impl Taggart { pub fn new (root: &impl AsRef) -> Usually { Ok(Self { @@ -66,49 +109,6 @@ impl Taggart { } } -#[derive(Ord, Eq, PartialEq, PartialOrd)] -pub struct Entry { - pub path: PathBuf, - pub depth: usize, - pub info: EntryInfo, -} - -#[derive(Ord, Eq, PartialEq, PartialOrd)] -pub enum EntryInfo { - Directory { - hash_file: Option<()>, - catalog_file: Option<()>, - artist_file: Option<()>, - release_file: Option<()>, - }, - Music { - hash: Arc, - file_type: &'static FileType, - artist: Option>, - album: Option>, - track: Option, - title: Option>, - date: Option>, - year: Option, - people: Option>>, - publisher: Option>, - key: Option>, - bpm: Option>, - invalid: bool, - }, - Image { - hash: Arc, - file_type: &'static FileType, - title: Option, - author: Option, - invalid: bool, - }, - Unknown { - hash: Arc, - file_type: &'static FileType, - } -} - impl Entry { pub fn new (root: &impl AsRef, entry: &DirEntry) -> Perhaps { println!("{}", entry.path().display()); @@ -133,51 +133,10 @@ impl Entry { })) } fn new_file (root: &impl AsRef, entry: &DirEntry) -> Perhaps { - let bytes = read(entry.path())?; - let hash = hex::encode(xxh3_64(&bytes).to_be_bytes()); - let file_type = FileType::try_from_reader(&*bytes)?; - let mime_type = file_type.media_types().get(0); - return Ok(Some(Self { + Ok(Some(Self { depth: entry.depth(), - path: entry.path().strip_prefix(root.as_ref())?.into(), - info: match mime_type { - Some(&"audio/mpeg3") => { - let id3 = Tag::read_from_path(entry.path())?; - EntryInfo::Music { - file_type, - hash: hash.into(), - 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()), - date: None, - year: id3.year().map(|x|x.into()), - people: None, - publisher: None, - key: None, - bpm: None, - invalid: false, - } - }, - Some(&"image/png") => EntryInfo::Image { - file_type, - hash: hash.into(), - title: None, - author: None, - invalid: false, - }, - Some(&"image/jpeg") => EntryInfo::Image { - file_type, - hash: hash.into(), - title: None, - author: None, - invalid: false, - }, - _ => EntryInfo::Unknown { - file_type, - hash: hash.into(), - } - }, + path: entry.path().strip_prefix(root.as_ref())?.into(), + info: EntryInfo::new(&read(entry.path())?)? })) } pub fn is_dir (&self) -> bool { @@ -221,3 +180,80 @@ 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(), + 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()), + date: None, + people: None, + publisher: None, + key: None, + bpm: None, + invalid: false, + }) + } + // 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, + }) + } + // PNG + if bytes.starts_with(&[0x89, b'P', b'N', b'G', 0x0D, 0x0A, 0x1A, 0x0A]) { + return Ok(Self::Image { + file_type: None, + hash: hex::encode(xxh3_64(&bytes).to_be_bytes()).into(), + title: None, + author: None, + invalid: false, + }) + } + // JPG + if bytes.starts_with(&[0xFF, 0xD8, 0xFF, 0xDB]) + || bytes.starts_with(&[0xFF, 0xD8, 0xFF, 0xE0, + 0x00, 0x10, 0x4A, 0x46, + 0x49, 0x46, 0x00, 0x01]) + || bytes.starts_with(&[0xFF, 0xD8, 0xFF, 0xEE]) + || (bytes.starts_with(&[0xFF, 0xD8, 0xFF, 0xE1]) && + bytes.get(6) == Some(&0x45) && bytes.get(7) == Some(&0x78) && + bytes.get(8) == Some(&0x69) && bytes.get(9) == Some(&0x66) && + bytes.get(10) == Some(&0x00) && bytes.get(11) == Some(&0x00)) + { + return Ok(Self::Image { + file_type: None, + hash: hex::encode(xxh3_64(&bytes).to_be_bytes()).into(), + title: None, + author: None, + invalid: false, + }) + } + Ok(Self::Unknown { + file_type: None, + hash: hex::encode(xxh3_64(&bytes).to_be_bytes()).into(), + }) + } +} diff --git a/src/view.rs b/src/view.rs index 186ac39..6f39664 100644 --- a/src/view.rs +++ b/src/view.rs @@ -101,7 +101,7 @@ impl<'a> Content for TreeTable<'a> { to.fill_bg(fill, Color::Rgb(192, 128, 0)); let fill = [area.x() + x as u16, y, w, 1]; to.fill_bg(fill, Color::Rgb(224, 192, 0)); - if let Some((index, value)) = &self.0.editing { + if let Some((_index, value)) = &self.0.editing { let x = area.x() + if x > 0 { x + 1 } else { x } as u16; to.blit(&value, x, y, None) }