use bytes::Bytes; use domain::{ errors::DomainError, ports::MetadataExtractorPort, value_objects::{MetadataValue, StructuredData}, }; use nom_exif::{ExifIter, MediaParser, MediaSource, TrackInfo}; use std::io::Cursor; pub struct NomExifExtractor; impl MetadataExtractorPort for NomExifExtractor { fn extract(&self, bytes: &Bytes) -> Result { if bytes.is_empty() { return Ok(StructuredData::new()); } let ms = match MediaSource::seekable(Cursor::new(bytes.as_ref())) { Ok(ms) => ms, Err(_) => return Ok(StructuredData::new()), }; let mut parser = MediaParser::new(); let mut data = StructuredData::new(); if ms.has_exif() { let iter: ExifIter = match parser.parse(ms) { Ok(iter) => iter, Err(_) => return Ok(data), }; for mut entry in iter { let tag_name = match entry.tag() { Some(t) => t.to_string(), None => continue, }; if tag_name.starts_with("Unknown(") { continue; } let value = match entry.take_result() { Ok(v) => v.to_string(), Err(_) => continue, }; if is_noisy_value(&value) { continue; } data.insert(tag_name, MetadataValue::String(value)); } } else { let track_info = match parser.parse::<_, _, TrackInfo>(ms) { Ok(info) => info, Err(_) => return Ok(data), }; for (key, val) in track_info { data.insert( format!("track:{}", key), MetadataValue::String(val.to_string()), ); } } Ok(data) } } fn is_noisy_value(v: &str) -> bool { v.starts_with("U16Array") || v.starts_with("U32Array") || v.starts_with("U8Array") || v.starts_with("URationalArray") || v.starts_with("Undefined") } #[cfg(test)] mod tests;