2022-03-15 17:20:50 +01:00
|
|
|
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;
|
|
|
|
|
2022-03-23 12:43:02 +01:00
|
|
|
// TODO: EXIF metadata is oftentimes a constant/enum value. What's the proper
|
2022-03-15 17:20:50 +01:00
|
|
|
// model for enum-like values in UpEnd?
|
|
|
|
|
|
|
|
impl Extractor for ExifExtractor {
|
|
|
|
fn get(
|
|
|
|
&self,
|
|
|
|
address: &Address,
|
|
|
|
connection: &UpEndConnection,
|
|
|
|
mut job_container: JobContainer,
|
|
|
|
) -> Result<Vec<Entry>> {
|
|
|
|
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!(
|
2022-03-28 20:06:00 +02:00
|
|
|
r#"Getting EXIF info from "{:}""#,
|
2022-03-15 17:20:50 +01:00
|
|
|
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<Entry> = exif
|
|
|
|
.fields()
|
2022-03-23 12:43:02 +01:00
|
|
|
.filter(|field| !matches!(field.value, exif::Value::Undefined(..)))
|
2022-03-15 17:20:50 +01:00
|
|
|
.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![])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|