From fc1ed81561222650e7132d05be3e1dac3790c8f0 Mon Sep 17 00:00:00 2001 From: Rasmus Kaj Date: Mon, 5 Mar 2018 00:29:36 +0100 Subject: [PATCH] Store width and height of images. Use it to calculate width and height of small images in lists. Since the old database should be migratable to the new, the width and height fields are nullable. I aim to make them not nullable later, after all images in the database has got width and height. --- .../2018-03-03-142213_photo_size/down.sql | 2 + .../2018-03-03-142213_photo_size/up.sql | 6 +++ src/adm/findphotos.rs | 6 +++ src/adm/makepublic.rs | 37 ++----------------- src/main.rs | 9 ++--- src/models.rs | 30 +++++++++++++++ src/myexif.rs | 4 +- src/server/mod.rs | 10 +++-- src/server/views_by_date.rs | 9 ++++- templates/photo_link.rs.html | 2 +- 10 files changed, 67 insertions(+), 48 deletions(-) create mode 100644 migrations/2018-03-03-142213_photo_size/down.sql create mode 100644 migrations/2018-03-03-142213_photo_size/up.sql diff --git a/migrations/2018-03-03-142213_photo_size/down.sql b/migrations/2018-03-03-142213_photo_size/down.sql new file mode 100644 index 0000000..4582c6b --- /dev/null +++ b/migrations/2018-03-03-142213_photo_size/down.sql @@ -0,0 +1,2 @@ +ALTER TABLE photos DROP COLUMN width; +ALTER TABLE photos DROP COLUMN height; diff --git a/migrations/2018-03-03-142213_photo_size/up.sql b/migrations/2018-03-03-142213_photo_size/up.sql new file mode 100644 index 0000000..2f66ca3 --- /dev/null +++ b/migrations/2018-03-03-142213_photo_size/up.sql @@ -0,0 +1,6 @@ +-- Add width and height to photos +-- Intially make them nullable, to make it possible to apply the +-- migration to an existing database. A NOT NULL constraint should +-- be added later, when all photos has sizes. +ALTER TABLE photos ADD COLUMN width INTEGER; +ALTER TABLE photos ADD COLUMN height INTEGER; diff --git a/src/adm/findphotos.rs b/src/adm/findphotos.rs index 4410259..ce87f4b 100644 --- a/src/adm/findphotos.rs +++ b/src/adm/findphotos.rs @@ -27,9 +27,15 @@ fn save_photo( file_path: &str, exif: &ExifData, ) -> Result<(), Error> { + let width = exif.width + .ok_or(Error::Other(format!("Image {} missing width", file_path)))?; + let height = exif.height + .ok_or(Error::Other(format!("Image {} missing height", file_path)))?; let photo = match Photo::create_or_set_basics( db, file_path, + width as i32, + height as i32, exif.date(), exif.rotation()?, find_camera(db, exif)?, diff --git a/src/adm/makepublic.rs b/src/adm/makepublic.rs index e62aab4..cfa1df7 100644 --- a/src/adm/makepublic.rs +++ b/src/adm/makepublic.rs @@ -3,15 +3,10 @@ use diesel::pg::PgConnection; use diesel::prelude::*; use diesel::result::Error as DieselError; use diesel::update; -use models::{Modification, Photo}; -use photosdir::PhotosDir; +use models::Photo; use std::io::prelude::*; -pub fn one( - db: &PgConnection, - photodir: &PhotosDir, - tpath: &str, -) -> Result<(), Error> { +pub fn one(db: &PgConnection, tpath: &str) -> Result<(), Error> { use schema::photos::dsl::*; match update(photos.filter(path.eq(&tpath))) .set(is_public.eq(true)) @@ -22,15 +17,7 @@ pub fn one( Ok(()) } Err(DieselError::NotFound) => { - if !photodir.has_file(&tpath) { - return Err(Error::Other(format!( - "File {} does not exist", - tpath, - ))); - } - let photo = register_photo(db, tpath)?; - println!("New photo {:?} is public.", photo); - Ok(()) + Err(Error::Other(format!("File {} is not known", tpath))) } Err(error) => Err(error.into()), } @@ -38,26 +25,10 @@ pub fn one( pub fn by_file_list( db: &PgConnection, - photodir: &PhotosDir, list: In, ) -> Result<(), Error> { for line in list.lines() { - one(db, photodir, &line?)?; + one(db, &line?)?; } Ok(()) } - -fn register_photo( - db: &PgConnection, - tpath: &str, -) -> Result { - use schema::photos::dsl::{is_public, photos}; - let photo = match Photo::create_or_set_basics(db, tpath, None, 0, None)? { - Modification::Created(photo) - | Modification::Updated(photo) - | Modification::Unchanged(photo) => photo, - }; - update(photos.find(photo.id)) - .set(is_public.eq(true)) - .get_result::(db) -} diff --git a/src/main.rs b/src/main.rs index 9959111..5b41561 100644 --- a/src/main.rs +++ b/src/main.rs @@ -169,22 +169,19 @@ fn run(args: &ArgMatches) -> Result<(), Error> { Ok(()) } ("makepublic", Some(args)) => { - let pd = PhotosDir::new(photos_dir()); let db = get_db()?; match args.value_of("LIST") { Some("-") => { let list = io::stdin(); - makepublic::by_file_list(&db, &pd, list.lock())?; + makepublic::by_file_list(&db, list.lock())?; Ok(()) } Some(f) => { let list = File::open(f)?; let list = BufReader::new(list); - makepublic::by_file_list(&db, &pd, list) - } - None => { - makepublic::one(&db, &pd, args.value_of("IMAGE").unwrap()) + makepublic::by_file_list(&db, list) } + None => makepublic::one(&db, args.value_of("IMAGE").unwrap()), } } ("stats", Some(_args)) => show_stats(&get_db()?), diff --git a/src/models.rs b/src/models.rs index b2108f2..82bb4bc 100644 --- a/src/models.rs +++ b/src/models.rs @@ -4,6 +4,7 @@ use diesel::pg::PgConnection; use diesel::prelude::*; use diesel::result::Error as DieselError; use server::SizeTag; +use std::cmp::max; #[derive(AsChangeset, Clone, Debug, Identifiable, Queryable)] pub struct Photo { @@ -15,6 +16,8 @@ pub struct Photo { pub is_public: bool, pub camera_id: Option, pub attribution_id: Option, + pub width: Option, + pub height: Option, } use schema::photos; @@ -55,6 +58,8 @@ impl Photo { pub fn update_by_path( db: &PgConnection, file_path: &str, + newwidth: i32, + newheight: i32, exifdate: Option, exifrotation: i16, camera: &Option, @@ -69,6 +74,12 @@ impl Photo { { let mut change = false; // TODO Merge updates to one update statement! + if pic.width != Some(newwidth) || pic.height != Some(newheight) { + change = true; + pic = diesel::update(photos.find(pic.id)) + .set((width.eq(newwidth), height.eq(newheight))) + .get_result::(db)?; + } if exifdate.is_some() && exifdate != pic.date { change = true; pic = diesel::update(photos.find(pic.id)) @@ -102,6 +113,8 @@ impl Photo { pub fn create_or_set_basics( db: &PgConnection, file_path: &str, + newwidth: i32, + newheight: i32, exifdate: Option, exifrotation: i16, camera: Option, @@ -112,6 +125,8 @@ impl Photo { if let Some(result) = Self::update_by_path( db, file_path, + newwidth, + newheight, exifdate, exifrotation, &camera, @@ -123,6 +138,8 @@ impl Photo { path.eq(file_path), date.eq(exifdate), rotation.eq(exifrotation), + width.eq(newwidth), + height.eq(newheight), camera_id.eq(camera.map(|c| c.id)), )) .get_result::(db)?; @@ -193,6 +210,19 @@ impl Photo { use schema::cameras::dsl::cameras; self.camera_id.and_then(|i| cameras.find(i).first(db).ok()) } + pub fn get_size(&self, max_size: u32) -> Option<(u32, u32)> { + if let (Some(width), Some(height)) = (self.width, self.height) { + let scale = f64::from(max_size) / f64::from(max(width, height)); + let w = (scale * f64::from(width)) as u32; + let h = (scale * f64::from(height)) as u32; + match self.rotation { + _x @ 0...44 | _x @ 315...360 | _x @ 135...224 => Some((w, h)), + _ => Some((h, w)), + } + } else { + None + } + } } #[derive(Debug, Clone, Queryable)] diff --git a/src/myexif.rs b/src/myexif.rs index e3f4134..7400efe 100644 --- a/src/myexif.rs +++ b/src/myexif.rs @@ -14,8 +14,8 @@ pub struct ExifData { gpstime: Option<(u8, u8, u8)>, make: Option, model: Option, - width: Option, - height: Option, + pub width: Option, + pub height: Option, orientation: Option, latval: Option, longval: Option, diff --git a/src/server/mod.rs b/src/server/mod.rs index 93a2b27..be9b940 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -37,6 +37,8 @@ pub struct PhotoLink { pub title: Option, pub href: String, pub id: i32, + // Size should not be optional, but make it best-effort for now. + pub size: Option<(u32, u32)>, pub lable: Option, } @@ -48,6 +50,7 @@ impl PhotoLink { fn imgscore(p: &Photo) -> i16 { p.grade.unwrap_or(27) + if p.is_public { 38 } else { 0 } } + let photo = g.iter().max_by_key(|p| imgscore(p)).unwrap(); PhotoLink { title: None, href: format!( @@ -56,10 +59,8 @@ impl PhotoLink { g.last().map(|p| p.id).unwrap_or(0), g.first().map(|p| p.id).unwrap_or(0), ), - id: g.iter() - .max_by_key(|p| imgscore(p)) - .map(|p| p.id) - .unwrap_or(0), + id: photo.id, + size: photo.get_size(SizeTag::Small.px()), lable: { let from = g.last().and_then(|p| p.date); let to = g.first().and_then(|p| p.date); @@ -101,6 +102,7 @@ impl<'a> From<&'a Photo> for PhotoLink { title: None, href: format!("/img/{}", p.id), id: p.id, + size: p.get_size(SizeTag::Small.px()), lable: p.date.map(|d| format!("{}", d.format("%F %T"))), } } diff --git a/src/server/views_by_date.rs b/src/server/views_by_date.rs index 7f57153..4416f5d 100644 --- a/src/server/views_by_date.rs +++ b/src/server/views_by_date.rs @@ -1,4 +1,4 @@ -use super::{Link, PhotoLink}; +use super::{Link, PhotoLink, SizeTag}; use super::splitlist::links_by_time; use chrono::Duration as ChDuration; use chrono::naive::{NaiveDate, NaiveDateTime}; @@ -46,6 +46,7 @@ pub fn all_years<'mw>( } else { q.filter(date.is_null()) }; + let photo = photo.first::(c).unwrap(); PhotoLink { title: Some( year.map(|y| format!("{}", y)) @@ -53,7 +54,8 @@ pub fn all_years<'mw>( ), href: format!("/{}/", year.unwrap_or(0)), lable: Some(format!("{} images", count)), - id: photo.first::(c).unwrap().id, + id: photo.id, + size: photo.get_size(SizeTag::Small.px()), } }) .collect(); @@ -105,6 +107,7 @@ pub fn months_in_year<'mw>( href: format!("/{}/{}/", year, month), lable: Some(format!("{} pictures", count)), id: photo.id, + size: photo.get_size(SizeTag::Small.px()), } }) .collect(); @@ -158,6 +161,7 @@ pub fn days_in_month<'mw>( href: format!("/{}/{}/{}", year, month, day), lable: Some(format!("{} pictures", count)), id: photo.id, + size: photo.get_size(SizeTag::Small.px()), } }) .collect(); @@ -278,6 +282,7 @@ pub fn on_this_day<'mw>( href: format!("/{}/{}/{}", year, month, day), lable: Some(format!("{} pictures", count)), id: photo.id, + size: photo.get_size(SizeTag::Small.px()), } }) .collect::>(), diff --git a/templates/photo_link.rs.html b/templates/photo_link.rs.html index 78ad467..da18777 100644 --- a/templates/photo_link.rs.html +++ b/templates/photo_link.rs.html @@ -2,6 +2,6 @@ @(photo: &PhotoLink)
@if let Some(ref title) = photo.title {

@title

} - + Photo @photo.id @if let Some(ref d) = photo.lable {@d}