pinakes-core: improve media management features; various configuration improvements
Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: I2d1f04f13970d21c36067f30bc04a9176a6a6964
This commit is contained in:
parent
cfdc3d0622
commit
e02c15490e
31 changed files with 1167 additions and 197 deletions
|
|
@ -35,32 +35,38 @@ impl MetadataExtractor for ImageExtractor {
|
|||
meta.extra.insert("height".to_string(), h.to_string());
|
||||
}
|
||||
|
||||
// Camera make and model
|
||||
// Camera make and model - set both in top-level fields and extra
|
||||
if let Some(make) = exif_data.get_field(exif::Tag::Make, exif::In::PRIMARY) {
|
||||
let val = make.display_value().to_string();
|
||||
let val = make.display_value().to_string().trim().to_string();
|
||||
if !val.is_empty() {
|
||||
meta.camera_make = Some(val.clone());
|
||||
meta.extra.insert("camera_make".to_string(), val);
|
||||
}
|
||||
}
|
||||
if let Some(model) = exif_data.get_field(exif::Tag::Model, exif::In::PRIMARY) {
|
||||
let val = model.display_value().to_string();
|
||||
let val = model.display_value().to_string().trim().to_string();
|
||||
if !val.is_empty() {
|
||||
meta.camera_model = Some(val.clone());
|
||||
meta.extra.insert("camera_model".to_string(), val);
|
||||
}
|
||||
}
|
||||
|
||||
// Date taken
|
||||
// Date taken - parse EXIF date format (YYYY:MM:DD HH:MM:SS)
|
||||
if let Some(date) = exif_data
|
||||
.get_field(exif::Tag::DateTimeOriginal, exif::In::PRIMARY)
|
||||
.or_else(|| exif_data.get_field(exif::Tag::DateTime, exif::In::PRIMARY))
|
||||
{
|
||||
let val = date.display_value().to_string();
|
||||
if !val.is_empty() {
|
||||
// Try parsing EXIF format: "YYYY:MM:DD HH:MM:SS"
|
||||
if let Some(dt) = parse_exif_datetime(&val) {
|
||||
meta.date_taken = Some(dt);
|
||||
}
|
||||
meta.extra.insert("date_taken".to_string(), val);
|
||||
}
|
||||
}
|
||||
|
||||
// GPS coordinates
|
||||
// GPS coordinates - set both in top-level fields and extra
|
||||
if let (Some(lat), Some(lat_ref), Some(lon), Some(lon_ref)) = (
|
||||
exif_data.get_field(exif::Tag::GPSLatitude, exif::In::PRIMARY),
|
||||
exif_data.get_field(exif::Tag::GPSLatitudeRef, exif::In::PRIMARY),
|
||||
|
|
@ -69,6 +75,8 @@ impl MetadataExtractor for ImageExtractor {
|
|||
) && let (Some(lat_val), Some(lon_val)) =
|
||||
(dms_to_decimal(lat, lat_ref), dms_to_decimal(lon, lon_ref))
|
||||
{
|
||||
meta.latitude = Some(lat_val);
|
||||
meta.longitude = Some(lon_val);
|
||||
meta.extra
|
||||
.insert("gps_latitude".to_string(), format!("{lat_val:.6}"));
|
||||
meta.extra
|
||||
|
|
@ -211,3 +219,45 @@ fn dms_to_decimal(dms_field: &exif::Field, ref_field: &exif::Field) -> Option<f6
|
|||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Parse EXIF datetime format: "YYYY:MM:DD HH:MM:SS"
|
||||
fn parse_exif_datetime(s: &str) -> Option<chrono::DateTime<chrono::Utc>> {
|
||||
use chrono::NaiveDateTime;
|
||||
|
||||
// EXIF format is "YYYY:MM:DD HH:MM:SS"
|
||||
let s = s.trim().trim_matches('"');
|
||||
|
||||
// Try standard EXIF format
|
||||
if let Ok(dt) = NaiveDateTime::parse_from_str(s, "%Y:%m:%d %H:%M:%S") {
|
||||
return Some(dt.and_utc());
|
||||
}
|
||||
|
||||
// Try ISO format as fallback
|
||||
if let Ok(dt) = NaiveDateTime::parse_from_str(s, "%Y-%m-%d %H:%M:%S") {
|
||||
return Some(dt.and_utc());
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
/// 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.
|
||||
pub fn generate_perceptual_hash(path: &Path) -> Option<String> {
|
||||
use image_hasher::{HashAlg, HasherConfig};
|
||||
|
||||
// Open and decode the image
|
||||
let img = image::open(path).ok()?;
|
||||
|
||||
// Create hasher with DCT algorithm (good for finding similar images)
|
||||
let hasher = HasherConfig::new()
|
||||
.hash_alg(HashAlg::DoubleGradient)
|
||||
.hash_size(8, 8) // 64-bit hash
|
||||
.to_hasher();
|
||||
|
||||
// Generate hash
|
||||
let hash = hasher.hash_image(&img);
|
||||
|
||||
// Convert to hex string for storage
|
||||
Some(hash.to_base64())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,14 @@ pub struct ExtractedMetadata {
|
|||
pub description: Option<String>,
|
||||
pub extra: HashMap<String, String>,
|
||||
pub book_metadata: Option<ExtractedBookMetadata>,
|
||||
|
||||
// Photo-specific metadata
|
||||
pub date_taken: Option<chrono::DateTime<chrono::Utc>>,
|
||||
pub latitude: Option<f64>,
|
||||
pub longitude: Option<f64>,
|
||||
pub camera_make: Option<String>,
|
||||
pub camera_model: Option<String>,
|
||||
pub rating: Option<i32>,
|
||||
}
|
||||
|
||||
pub trait MetadataExtractor: Send + Sync {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue