use super::Extractor; use crate::{ addressing::Address, database::{ constants, entry::{Entry, EntryValue}, UpEndConnection, }, filesystem::FILE_MIME_KEY, util::jobs::{JobContainer, JobState}, }; use anyhow::{anyhow, Result}; pub struct ExifExtractor; // TODO: EXIF metadata is oftentimes a constant/enum value. What's the proper // model for enum-like values in UpEnd? impl Extractor for ExifExtractor { fn get( &self, address: &Address, connection: &UpEndConnection, mut job_container: JobContainer, ) -> Result> { if let Address::Hash(hash) = address { let is_photo = connection.retrieve_object(address)?.iter().any(|e| { if e.attribute == FILE_MIME_KEY { if let EntryValue::String(mime) = &e.value { return mime.starts_with("image"); } } false }); if !is_photo { return Ok(vec![]); } let files = connection.retrieve_file(hash)?; if let Some(file) = files.get(0) { let mut job_handle = job_container.add_job( None, &format!( "Getting EXIF info from \"{:}\"", file.path .components() .last() .unwrap() .as_os_str() .to_string_lossy() ), )?; let file = std::fs::File::open(&file.path)?; let mut bufreader = std::io::BufReader::new(&file); let exifreader = exif::Reader::new(); let exif = exifreader.read_from_container(&mut bufreader)?; let result: Vec = exif .fields() .flat_map(|field| { if let Some(tag_description) = field.tag.description() { let attribute = format!("EXIF_{}", field.tag.1); vec![ Entry { entity: address.clone(), attribute: attribute.clone(), value: match field.tag { exif::Tag::ExifVersion => { EntryValue::String(format!("{}", field.display_value())) } _ => EntryValue::guess_from(format!( "{}", field.display_value() )), }, }, Entry { entity: Address::Attribute(attribute), attribute: constants::LABEL_ATTR.into(), value: format!("EXIF: {}", tag_description).into(), }, ] } else { vec![] } }) .collect(); let _ = job_handle.update_state(JobState::Done); Ok(result) } else { Err(anyhow!("Couldn't find file for {hash:?}!")) } } else { Ok(vec![]) } } }