read id3 tags

This commit is contained in:
🪞👃🪞 2025-03-07 23:49:08 +02:00
parent 72bd6148d6
commit b29511c23e
6 changed files with 251 additions and 272 deletions

168
src/model.rs Normal file
View file

@ -0,0 +1,168 @@
use crate::*;
use walkdir::DirEntry;
use id3::{Tag, TagLike};
use std::io::Read;
pub struct Taggart {
pub root: PathBuf,
pub paths: Vec<Entry>,
pub cursor: usize,
pub offset: usize,
pub column: usize,
pub size: Measure<TuiOut>,
pub editing: Option<(usize, usize)>,
pub show_hash: bool,
}
#[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<str>,
file_type: &'static FileType,
artist: Option<Arc<str>>,
album: Option<Arc<str>>,
track: Option<u32>,
title: Option<Arc<str>>,
date: Option<Arc<str>>,
year: Option<i32>,
people: Option<Vec<Arc<str>>>,
publisher: Option<Arc<str>>,
key: Option<Arc<str>>,
bpm: Option<Arc<str>>,
invalid: bool,
},
Image {
hash: Arc<str>,
file_type: &'static FileType,
title: Option<String>,
author: Option<String>,
invalid: bool,
},
}
impl Entry {
pub fn new (root: &impl AsRef<Path>, entry: &DirEntry) -> Perhaps<Self> {
if entry.path().is_dir() {
Self::new_dir(root, entry)
} else if entry.path().is_file() {
Self::new_file(root, entry)
} else {
Ok(None)
}
}
fn new_dir (root: &impl AsRef<Path>, entry: &DirEntry) -> Perhaps<Self> {
Ok(Some(Self {
depth: entry.depth(),
path: entry.path().into(),
info: EntryInfo::Directory {
hash_file: None,
catalog_file: None,
artist_file: None,
release_file: None,
},
}))
}
fn new_file (root: &impl AsRef<Path>, entry: &DirEntry) -> Perhaps<Self> {
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 {
depth: entry.depth(),
path: entry.path().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,
},
_ => return Ok(None)
},
}))
}
pub fn short_path (&self, root: &impl AsRef<Path>) -> Usually<&Path> {
Ok(self.path.strip_prefix(root.as_ref())?)
}
pub fn is_dir (&self) -> bool {
matches!(self.info, EntryInfo::Directory { .. })
}
pub fn is_mus (&self) -> bool {
matches!(self.info, EntryInfo::Music { .. })
}
pub fn is_img (&self) -> bool {
matches!(self.info, EntryInfo::Image { .. })
}
pub fn hash (&self) -> Option<Arc<str>> {
match self.info {
EntryInfo::Image { ref hash, .. } => Some(hash.clone()),
EntryInfo::Music { ref hash, .. } => Some(hash.clone()),
_ => None
}
}
pub fn artist (&self) -> Option<Arc<str>> {
match self.info {
EntryInfo::Music { ref artist, .. } => artist.clone(),
_ => None
}
}
pub fn album (&self) -> Option<Arc<str>> {
match self.info {
EntryInfo::Music { ref album, .. } => album.clone(),
_ => None
}
}
pub fn title (&self) -> Option<Arc<str>> {
match self.info {
EntryInfo::Music { ref title, .. } => title.clone(),
_ => None
}
}
}