pinakes-core: update remaining modules and tests

Signed-off-by: NotAShelf <raf@notashelf.dev>
Change-Id: I9e0ff5ea33a5cf697473423e88f167ce6a6a6964
This commit is contained in:
raf 2026-03-08 00:42:29 +03:00
commit 3d9f8933d2
Signed by: NotAShelf
GPG key ID: 29D95B64378DB4BF
44 changed files with 1207 additions and 578 deletions

View file

@ -29,7 +29,7 @@ impl MetadataExtractor for AudioExtractor {
meta.artist = tag.artist().map(|s| s.to_string());
meta.album = tag.album().map(|s| s.to_string());
meta.genre = tag.genre().map(|s| s.to_string());
meta.year = tag.date().map(|ts| ts.year as i32);
meta.year = tag.date().map(|ts| i32::from(ts.year));
}
if let Some(tag) = tagged_file

View file

@ -15,11 +15,11 @@ impl MetadataExtractor for ImageExtractor {
let file = std::fs::File::open(path)?;
let mut buf_reader = std::io::BufReader::new(&file);
let exif_data =
match exif::Reader::new().read_from_container(&mut buf_reader) {
Ok(exif) => exif,
Err(_) => return Ok(meta),
};
let Ok(exif_data) =
exif::Reader::new().read_from_container(&mut buf_reader)
else {
return Ok(meta);
};
// Image dimensions
if let Some(width) = exif_data
@ -226,7 +226,7 @@ impl MetadataExtractor for ImageExtractor {
fn field_to_u32(field: &exif::Field) -> Option<u32> {
match &field.value {
exif::Value::Long(v) => v.first().copied(),
exif::Value::Short(v) => v.first().map(|&x| x as u32),
exif::Value::Short(v) => v.first().map(|&x| u32::from(x)),
_ => None,
}
}
@ -274,9 +274,11 @@ fn parse_exif_datetime(s: &str) -> Option<chrono::DateTime<chrono::Utc>> {
}
/// Generate a perceptual hash for an image file.
///
/// Uses DCT (Discrete Cosine Transform) hash algorithm for robust similarity
/// detection. Returns a hex-encoded hash string, or None if the image cannot be
/// processed.
#[must_use]
pub fn generate_perceptual_hash(path: &Path) -> Option<String> {
use image_hasher::{HashAlg, HasherConfig};

View file

@ -34,13 +34,25 @@ pub struct ExtractedMetadata {
}
pub trait MetadataExtractor: Send + Sync {
/// Extract metadata from a file at the given path.
///
/// # Errors
///
/// Returns an error if the file cannot be read or parsed.
fn extract(&self, path: &Path) -> Result<ExtractedMetadata>;
fn supported_types(&self) -> Vec<MediaType>;
}
/// Extract metadata from a file using the appropriate extractor for the given
/// media type.
///
/// # Errors
///
/// Returns an error if no extractor supports the media type, or if extraction
/// fails.
pub fn extract_metadata(
path: &Path,
media_type: MediaType,
media_type: &MediaType,
) -> Result<ExtractedMetadata> {
let extractors: Vec<Box<dyn MetadataExtractor>> = vec![
Box::new(audio::AudioExtractor),
@ -51,7 +63,7 @@ pub fn extract_metadata(
];
for extractor in &extractors {
if extractor.supported_types().contains(&media_type) {
if extractor.supported_types().contains(media_type) {
return extractor.extract(path);
}
}

View file

@ -53,7 +53,7 @@ fn extract_mkv(path: &Path) -> Result<ExtractedMetadata> {
matroska::Settings::Audio(a) => {
meta.extra.insert(
"sample_rate".to_string(),
format!("{} Hz", a.sample_rate as u32),
format!("{:.0} Hz", a.sample_rate),
);
meta
.extra
@ -64,7 +64,7 @@ fn extract_mkv(path: &Path) -> Result<ExtractedMetadata> {
.insert("audio_codec".to_string(), track.codec_id.clone());
}
},
_ => {},
matroska::Settings::None => {},
}
}
@ -99,7 +99,7 @@ fn extract_mp4(path: &Path) -> Result<ExtractedMetadata> {
meta.genre = tag
.genre()
.map(|s: std::borrow::Cow<'_, str>| s.to_string());
meta.year = tag.date().map(|ts| ts.year as i32);
meta.year = tag.date().map(|ts| i32::from(ts.year));
}
let properties = tagged_file.properties();