use std::sync::Arc; use super::Extractor; use crate::{ addressing::Address, database::{ constants, entry::{Entry, EntryValue}, stores::{fs::FILE_MIME_KEY, UpStore}, UpEndConnection, }, util::jobs::{JobContainer, JobState}, }; use anyhow::{anyhow, Result}; pub struct ID3Extractor; impl Extractor for ID3Extractor { fn get( &self, address: &Address, _connection: &UpEndConnection, store: Arc>, mut job_container: JobContainer, ) -> Result> { if let Address::Hash(hash) = address { let files = store.retrieve(hash)?; if let Some(file) = files.get(0) { let file_path = file.get_file_path(); let mut job_handle = job_container.add_job( None, &format!( r#"Getting ID3 info from "{:}""#, file_path .components() .last() .unwrap() .as_os_str() .to_string_lossy() ), )?; let tags = id3::Tag::read_from_path(file_path)?; let result: Vec = tags .frames() .flat_map(|frame| match frame.content() { id3::Content::Text(text) => vec![ Entry { entity: address.clone(), attribute: format!("ID3_{}", frame.id()), value: match frame.id() { "TYER" | "TBPM" => EntryValue::guess_from(text), _ => text.clone().into(), }, provenance: "SYSTEM EXTRACTOR".to_string(), timestamp: chrono::Utc::now().naive_utc(), }, Entry { entity: Address::Attribute(format!("ID3_{}", frame.id())), attribute: constants::LABEL_ATTR.into(), value: format!("ID3: {}", frame.name()).into(), provenance: "SYSTEM EXTRACTOR".to_string(), timestamp: chrono::Utc::now().naive_utc(), }, ], _ => vec![], }) .collect(); let _ = job_handle.update_state(JobState::Done); Ok(result) } else { Err(anyhow!("Couldn't find file for {hash:?}!")) } } else { Ok(vec![]) } } fn is_needed(&self, address: &Address, connection: &UpEndConnection) -> Result { let is_audio = connection.retrieve_object(address)?.iter().any(|e| { if e.attribute == FILE_MIME_KEY { if let EntryValue::String(mime) = &e.value { return mime.starts_with("audio") || mime == "application/x-riff"; } } false }); if !is_audio { return Ok(false); } let is_extracted = !connection .query(format!("(matches @{} (contains \"ID3\") ?)", address).parse()?)? .is_empty(); if is_extracted { return Ok(false); } Ok(true) } }