From 747abab922d0e9f2d5020769870ff0a1194c4fdb Mon Sep 17 00:00:00 2001 From: unspeaker Date: Mon, 3 Mar 2025 21:05:18 +0200 Subject: [PATCH] collect hashes and file types --- Cargo.lock | 93 +++++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 4 +++ src/keys.rs | 24 ++++++++++++++ src/main.rs | 89 +++++++++++++++++++++++++++++++++----------------- src/view.rs | 4 +-- 5 files changed, 183 insertions(+), 31 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d70e239..a33caaa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -109,6 +109,12 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "better-panic" version = "0.3.0" @@ -125,6 +131,15 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + [[package]] name = "bumpalo" version = "3.17.0" @@ -238,6 +253,15 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + [[package]] name = "crc32fast" version = "1.4.2" @@ -278,6 +302,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + [[package]] name = "darling" version = "0.20.10" @@ -313,6 +347,16 @@ dependencies = [ "syn", ] +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + [[package]] name = "either" version = "1.14.0" @@ -378,6 +422,16 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "getrandom" version = "0.2.15" @@ -412,6 +466,12 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "id3" version = "1.16.2" @@ -887,6 +947,17 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "signal-hook" version = "0.3.17" @@ -978,13 +1049,17 @@ dependencies = [ name = "taggart" version = "0.1.0" dependencies = [ + "base64", "clap", "file_type", + "hex", "id3", "moku", "pad", + "sha2", "tek_tui", "walkdir", + "xxhash-rust", ] [[package]] @@ -1051,6 +1126,12 @@ dependencies = [ "syn", ] +[[package]] +name = "typenum" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" + [[package]] name = "typewit" version = "1.11.0" @@ -1107,6 +1188,12 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + [[package]] name = "walkdir" version = "2.5.0" @@ -1303,6 +1390,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "xxhash-rust" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdd20c5420375476fbd4394763288da7eb0cc0b8c11deed431a91562af7335d3" + [[package]] name = "zerocopy" version = "0.7.35" diff --git a/Cargo.toml b/Cargo.toml index d0a087f..7281639 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,3 +11,7 @@ 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" diff --git a/src/keys.rs b/src/keys.rs index 00c82b5..9b2591a 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -53,6 +53,30 @@ impl Handle for Taggart { }) => { self.column = self.column + 1; }, + Event::Key(KeyEvent { + code: KeyCode::Enter, + kind: KeyEventKind::Press, + modifiers: KeyModifiers::NONE, + state: KeyEventState::NONE + }) => { + self.editing = Some((self.cursor, self.column)); + }, + Event::Key(KeyEvent { + code: KeyCode::Esc, + kind: KeyEventKind::Press, + modifiers: KeyModifiers::NONE, + state: KeyEventState::NONE + }) => { + self.editing = None; + }, + Event::Key(KeyEvent { + code: KeyCode::Tab, + kind: KeyEventKind::Press, + modifiers: KeyModifiers::NONE, + state: KeyEventState::NONE + }) => { + self.show_hash = !self.show_hash; + }, _ => {} } if self.cursor < x_min { diff --git a/src/main.rs b/src/main.rs index 41cacf0..47c05cc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,14 +1,21 @@ #![feature(os_str_display)] -use tek_tui::*; -use tek_tui::tek_input::*; -use tek_tui::tek_output::*; -use crate::crossterm::event::{Event, KeyEvent, KeyCode, KeyModifiers, KeyEventState, KeyEventKind}; -use clap::{arg, command, value_parser, ArgAction, Command}; -use walkdir::WalkDir; use std::sync::{Arc, RwLock}; use std::path::{Path, PathBuf}; use std::env::current_dir; +use std::fs::read; + +use tek_tui::*; +use tek_tui::tek_output::*; +use tek_tui::tek_input::*; +use crate::crossterm::event::{Event, KeyEvent, KeyCode, KeyModifiers, KeyEventState, KeyEventKind}; + +use clap::{arg, command, value_parser, ArgAction, Command}; +use walkdir::WalkDir; +use sha2::{Sha256, Digest}; +use xxhash_rust::xxh3::xxh3_64; +use base64::prelude::*; +use file_type::FileType; mod keys; mod view; @@ -25,20 +32,24 @@ fn cli () -> clap::Command { .arg(arg!([path] "Path to root directory").value_parser(value_parser!(PathBuf))) } struct Taggart { - root: PathBuf, - paths: Vec, - cursor: usize, - offset: usize, - column: usize, - size: Measure, + root: PathBuf, + paths: Vec, + cursor: usize, + offset: usize, + column: usize, + size: Measure, + editing: Option<(usize, usize)>, + show_hash: bool, } #[derive(Ord, Eq, PartialEq, PartialOrd, Default)] struct Entry { - path: PathBuf, - is_dir: bool, - is_mus: bool, - is_img: bool, - depth: usize, + path: PathBuf, + is_dir: bool, + is_mus: bool, + is_img: bool, + depth: usize, + hash: Option, + file_type: Option<&'static FileType>, } fn main () -> Usually<()> { @@ -55,6 +66,18 @@ impl Taggart { } else { current_dir()? }; + Ok(Self { + paths: Self::collect(&root)?, + root, + cursor: 0, + offset: 0, + column: 0, + size: Measure::new(), + editing: None, + show_hash: false, + }) + } + fn collect (root: &impl AsRef) -> Usually> { let mut paths = vec![]; for entry in WalkDir::new(&root) .into_iter() @@ -70,28 +93,36 @@ impl Taggart { } let depth = entry.depth(); let path = entry.into_path(); - let (is_dir, is_mus, is_img) = if path.is_dir() { - (true, false, false) + let (is_dir, is_mus, is_img, hash, file_type) = if path.is_dir() { + (true, false, false, None, None) } else { - (false, false, false) + let bytes = read(&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); + let is_mus = match mime_type { + Some(&"audio/mpeg3") => true, + _ => false, + }; + let is_img = match mime_type { + Some(&"image/png") => true, + _ => false, + }; + println!("{hash} {file_type:?} ({}b)\n{}\n", bytes.len(), path.display()); + (false, is_mus, is_img, Some(hash), Some(file_type)) }; paths.push(Entry { path: path.strip_prefix(&root)?.into(), is_dir: path.is_dir(), is_mus: false, is_img: false, - depth + depth, + hash, + file_type }); } paths.sort(); - Ok(Self { - root, - paths, - cursor: 0, - offset: 0, - column: 0, - size: Measure::new(), - }) + Ok(paths) } } diff --git a/src/view.rs b/src/view.rs index 58f98a3..3feffe5 100644 --- a/src/view.rs +++ b/src/view.rs @@ -2,7 +2,6 @@ use crate::*; use tek_tui::ratatui::{style::{Color, Style}, prelude::Stylize}; use pad::PadStr; - fn table_row (label: &str, artist: &str, album: &str, track: &str, title: &str) -> String { let label = label.pad_to_width(COLUMN_WIDTHS[0] as usize); let artist = artist.pad_to_width(COLUMN_WIDTHS[1] as usize); @@ -11,6 +10,7 @@ fn table_row (label: &str, artist: &str, album: &str, track: &str, title: &str) let title = title.pad_to_width(COLUMN_WIDTHS[4] as usize); format!("{label}│{artist}╎{album}╎{track}╎{title}") } + fn status_bar (content: impl Content) -> impl Content { Fixed::y(1, Fill::x(Tui::bold(true, Tui::fg_bg(Color::Rgb(0,0,0), Color::Rgb(255,255,255), content)))) } @@ -49,7 +49,7 @@ impl<'a> Content for TreeTable<'a> { for (index, fragment) in entry.path.iter().enumerate() { if index == entry.depth - 1 { let cursor = if selected { ">" } else { " " }; - let icon = if entry.is_dir {"+"} else {" "}; + let icon = if entry.is_dir {"+"} else if entry.is_img {"I"} else if entry.is_mus {"M"} else {" "}; let name = fragment.display(); let indent = "".pad_to_width((entry.depth - 1) * 2); let label = table_row(&format!("{cursor} {indent}{icon} {name}"), "", "", "", "");