Add Exif storing and update to Metadata endpoint

This commit is contained in:
Cameron
2025-12-17 16:55:48 -05:00
parent 4851f64229
commit 4082f1fdb8
13 changed files with 851 additions and 8 deletions

View File

@@ -34,6 +34,7 @@ use rayon::prelude::*;
use crate::auth::login;
use crate::data::*;
use crate::database::*;
use crate::database::models::InsertImageExif;
use crate::files::{
RealFileSystem, RefreshThumbnailsMessage, is_image_or_video, is_valid_full_path, move_file,
};
@@ -53,6 +54,7 @@ mod auth;
mod data;
mod database;
mod error;
mod exif;
mod files;
mod state;
mod tags;
@@ -146,17 +148,29 @@ async fn get_file_metadata(
request: HttpRequest,
path: web::Query<ThumbnailRequest>,
app_state: Data<AppState>,
exif_dao: Data<Mutex<Box<dyn ExifDao>>>,
) -> impl Responder {
let tracer = global_tracer();
let context = extract_context_from_request(&request);
let mut span = tracer.start_with_context("get_file_metadata", &context);
match is_valid_full_path(&app_state.base_path, &path.path, false)
let full_path = is_valid_full_path(&app_state.base_path, &path.path, false);
match full_path
.ok_or_else(|| ErrorKind::InvalidData.into())
.and_then(File::open)
.and_then(|file| file.metadata())
{
Ok(metadata) => {
let response: MetadataResponse = metadata.into();
let mut response: MetadataResponse = metadata.into();
// Query EXIF data if available
if let Ok(mut dao) = exif_dao.lock() {
if let Ok(Some(exif)) = dao.get_exif(&path.path) {
response.exif = Some(exif.into());
}
}
span.add_event(
"Metadata fetched",
vec![KeyValue::new("file", path.path.clone())],
@@ -181,6 +195,7 @@ async fn upload_image(
request: HttpRequest,
mut payload: mp::Multipart,
app_state: Data<AppState>,
exif_dao: Data<Mutex<Box<dyn ExifDao>>>,
) -> impl Responder {
let tracer = global_tracer();
let context = extract_context_from_request(&request);
@@ -224,11 +239,12 @@ async fn upload_image(
.span_builder("file write")
.start_with_context(&tracer, &context);
if !full_path.is_file() && is_image_or_video(&full_path) {
let uploaded_path = if !full_path.is_file() && is_image_or_video(&full_path) {
let mut file = File::create(&full_path).unwrap();
file.write_all(&file_content).unwrap();
info!("Uploaded: {:?}", full_path);
full_path
} else {
warn!("File already exists: {:?}", full_path);
@@ -245,8 +261,56 @@ async fn upload_image(
);
info!("Uploaded: {}", new_path);
let mut file = File::create(new_path).unwrap();
let new_path_buf = PathBuf::from(&new_path);
let mut file = File::create(&new_path_buf).unwrap();
file.write_all(&file_content).unwrap();
new_path_buf
};
// Extract and store EXIF data if file supports it
if exif::supports_exif(&uploaded_path) {
let relative_path = uploaded_path
.strip_prefix(&app_state.base_path)
.expect("Error stripping base path prefix")
.to_str()
.unwrap()
.to_string();
match exif::extract_exif_from_path(&uploaded_path) {
Ok(exif_data) => {
let timestamp = Utc::now().timestamp();
let insert_exif = InsertImageExif {
file_path: relative_path.clone(),
camera_make: exif_data.camera_make,
camera_model: exif_data.camera_model,
lens_model: exif_data.lens_model,
width: exif_data.width,
height: exif_data.height,
orientation: exif_data.orientation,
gps_latitude: exif_data.gps_latitude,
gps_longitude: exif_data.gps_longitude,
gps_altitude: exif_data.gps_altitude,
focal_length: exif_data.focal_length,
aperture: exif_data.aperture,
shutter_speed: exif_data.shutter_speed,
iso: exif_data.iso,
date_taken: exif_data.date_taken,
created_time: timestamp,
last_modified: timestamp,
};
if let Ok(mut dao) = exif_dao.lock() {
if let Err(e) = dao.store_exif(insert_exif) {
error!("Failed to store EXIF data for {}: {:?}", relative_path, e);
} else {
debug!("EXIF data stored for {}", relative_path);
}
}
}
Err(e) => {
debug!("No EXIF data or error extracting from {}: {:?}", uploaded_path.display(), e);
}
}
}
} else {
error!("Invalid path for upload: {:?}", full_path);
@@ -645,6 +709,7 @@ fn main() -> std::io::Result<()> {
let user_dao = SqliteUserDao::new();
let favorites_dao = SqliteFavoriteDao::new();
let tag_dao = SqliteTagDao::default();
let exif_dao = SqliteExifDao::new();
App::new()
.wrap(middleware::Logger::default())
.service(web::resource("/login").route(web::post().to(login::<SqliteUserDao>)))
@@ -673,6 +738,7 @@ fn main() -> std::io::Result<()> {
favorites_dao,
))))
.app_data::<Data<Mutex<SqliteTagDao>>>(Data::new(Mutex::new(tag_dao)))
.app_data::<Data<Mutex<Box<dyn ExifDao>>>>(Data::new(Mutex::new(Box::new(exif_dao))))
.wrap(prometheus.clone())
})
.bind(dotenv::var("BIND_URL").unwrap())?