Add camera support.
Store and show which camera each picture was taken with (if present in exif data).
This commit is contained in:
parent
97f299b54f
commit
6ed42c0be7
2
migrations/20160808022325_create_cameras_table/down.sql
Normal file
2
migrations/20160808022325_create_cameras_table/down.sql
Normal file
@ -0,0 +1,2 @@
|
||||
ALTER TABLE photos DROP COLUMN camera_id;
|
||||
DROP TABLE cameras;
|
9
migrations/20160808022325_create_cameras_table/up.sql
Normal file
9
migrations/20160808022325_create_cameras_table/up.sql
Normal file
@ -0,0 +1,9 @@
|
||||
CREATE TABLE cameras (
|
||||
id SERIAL PRIMARY KEY,
|
||||
manufacturer VARCHAR NOT NULL,
|
||||
model VARCHAR NOT NULL
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX cameras_idx ON cameras (manufacturer, model);
|
||||
|
||||
ALTER TABLE photos ADD COLUMN camera_id INTEGER REFERENCES cameras (id);
|
@ -16,7 +16,7 @@ use std::path::Path;
|
||||
use dotenv::dotenv;
|
||||
use diesel::pg::PgConnection;
|
||||
use self::diesel::prelude::*;
|
||||
use rphotos::models::{Modification, Photo};
|
||||
use rphotos::models::{Modification, Photo, Camera};
|
||||
|
||||
mod env;
|
||||
use env::{dburl, photos_dir};
|
||||
@ -61,8 +61,9 @@ fn save_photo(db: &PgConnection,
|
||||
exif: &ExifData)
|
||||
-> FindPhotoResult<()> {
|
||||
let photo = match try!(Photo::create_or_set_basics(db, file_path,
|
||||
Some(try!(find_date(&exif))),
|
||||
try!(find_rotation(&exif)))) {
|
||||
Some(try!(find_date(&exif))),
|
||||
try!(find_rotation(&exif)),
|
||||
try!(find_camera(db, exif)))) {
|
||||
Modification::Created(photo) => {
|
||||
info!("Created {:?}", photo);
|
||||
photo
|
||||
@ -103,6 +104,19 @@ fn save_photo(db: &PgConnection,
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn find_camera(db: &PgConnection, exif: &ExifData) -> FindPhotoResult<Option<Camera>> {
|
||||
if let (Some(maketag), Some(modeltag)) = (find_entry(exif, &ExifTag::Make),
|
||||
find_entry(exif, &ExifTag::Model)) {
|
||||
if let (TagValue::Ascii(make), TagValue::Ascii(model)) =
|
||||
(maketag.clone().value, modeltag.clone().value) {
|
||||
let cam = try!(Camera::get_or_create(db, &make, &model));
|
||||
return Ok(Some(cam));
|
||||
}
|
||||
// TODO else Err(...?)
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
type FindPhotoResult<T> = Result<T, FindPhotoError>;
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -39,7 +39,7 @@ use diesel::prelude::*;
|
||||
use diesel::pg::PgConnection;
|
||||
use chrono::naive::date::NaiveDate;
|
||||
|
||||
use rphotos::models::{Person, Photo, Place, Tag};
|
||||
use rphotos::models::{Camera, Person, Photo, Place, Tag};
|
||||
|
||||
mod env;
|
||||
use env::{dburl, env_or, jwt_key, photos_dir};
|
||||
@ -444,6 +444,12 @@ fn photo_details<'mw>(req: &mut Request,
|
||||
}
|
||||
}
|
||||
},
|
||||
camera: Option<Camera> = {
|
||||
use rphotos::schema::cameras::dsl::*;
|
||||
tphoto.camera_id.map(|i| {
|
||||
cameras.find(i).first(c).unwrap()
|
||||
})
|
||||
},
|
||||
time: String = match tphoto.date {
|
||||
Some(d) => d.format("%T").to_string(),
|
||||
None => "".to_string()
|
||||
|
@ -4,6 +4,7 @@ use diesel::pg::PgConnection;
|
||||
use diesel::result::Error as DieselError;
|
||||
|
||||
#[derive(Debug, Clone, Queryable)]
|
||||
#[belongs_to(Camera)]
|
||||
pub struct Photo {
|
||||
pub id: i32,
|
||||
pub path: String,
|
||||
@ -11,6 +12,7 @@ pub struct Photo {
|
||||
pub grade: Option<i16>,
|
||||
pub rotation: i16,
|
||||
pub is_public: bool,
|
||||
pub camera_id: Option<i32>,
|
||||
}
|
||||
|
||||
// NaiveDateTime isn't Encodable, so we have to implement this by hand.
|
||||
@ -40,6 +42,7 @@ pub struct NewPhoto<'a> {
|
||||
pub path: &'a str,
|
||||
pub date: Option<NaiveDateTime>,
|
||||
pub rotation: i16,
|
||||
pub camera_id: Option<i32>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -73,16 +76,19 @@ impl Photo {
|
||||
pub fn create_or_set_basics(db: &PgConnection,
|
||||
file_path: &str,
|
||||
exifdate: Option<NaiveDateTime>,
|
||||
exifrotation: i16)
|
||||
exifrotation: i16,
|
||||
camera: Option<Camera>)
|
||||
-> Result<Modification<Photo>, DieselError> {
|
||||
use diesel;
|
||||
use diesel::prelude::*;
|
||||
use schema::photos::dsl::*;
|
||||
let cameraid = camera.map(|c| c.id);
|
||||
if let Some(mut pic) =
|
||||
try!(photos.filter(path.eq(&file_path.to_string()))
|
||||
.first::<Photo>(db)
|
||||
.optional()) {
|
||||
let mut change = false;
|
||||
// TODO Merge updates to one update statement!
|
||||
if exifdate.is_some() && exifdate != pic.date {
|
||||
change = true;
|
||||
pic = try!(diesel::update(photos.find(pic.id))
|
||||
@ -95,6 +101,12 @@ impl Photo {
|
||||
.set(rotation.eq(exifrotation))
|
||||
.get_result::<Photo>(db));
|
||||
}
|
||||
if cameraid != pic.camera_id {
|
||||
change = true;
|
||||
pic = try!(diesel::update(photos.find(pic.id))
|
||||
.set(camera_id.eq(cameraid))
|
||||
.get_result::<Photo>(db));
|
||||
}
|
||||
Ok(if change {
|
||||
Modification::Updated(pic)
|
||||
} else {
|
||||
@ -105,6 +117,7 @@ impl Photo {
|
||||
path: &file_path,
|
||||
date: exifdate,
|
||||
rotation: exifrotation,
|
||||
camera_id: cameraid,
|
||||
};
|
||||
let pic = try!(diesel::insert(&pic)
|
||||
.into(photos)
|
||||
@ -221,3 +234,42 @@ pub struct NewUser<'a> {
|
||||
pub username: &'a str,
|
||||
pub password: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Identifiable, RustcEncodable, Queryable)]
|
||||
#[has_many(photos)]
|
||||
pub struct Camera {
|
||||
pub id: i32,
|
||||
pub manufacturer: String,
|
||||
pub model: String,
|
||||
}
|
||||
use super::schema::cameras;
|
||||
#[insertable_into(cameras)]
|
||||
pub struct NewCamera {
|
||||
pub manufacturer: String,
|
||||
pub model: String,
|
||||
}
|
||||
|
||||
impl Camera {
|
||||
pub fn get_or_create(db: &PgConnection,
|
||||
make: &str,
|
||||
modl: &str)
|
||||
-> Result<Camera, DieselError> {
|
||||
use diesel;
|
||||
use diesel::prelude::*;
|
||||
use schema::cameras::dsl::*;
|
||||
if let Some(camera) = try!(cameras.filter(manufacturer.eq(make))
|
||||
.filter(model.eq(modl))
|
||||
.first::<Camera>(db)
|
||||
.optional()) {
|
||||
Ok(camera)
|
||||
} else {
|
||||
let camera = NewCamera {
|
||||
manufacturer: make.to_string(),
|
||||
model: modl.to_string(),
|
||||
};
|
||||
diesel::insert(&camera)
|
||||
.into(cameras)
|
||||
.get_result(db)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -194,7 +194,7 @@ fn main() {
|
||||
.unwrap();
|
||||
let date = find_image_date(attributes);
|
||||
photo = Some(match Photo::create_or_set_basics
|
||||
(&db, &file, date, angle)
|
||||
(&db, &file, date, angle, None)
|
||||
.expect("Create or update photo") {
|
||||
Modification::Created(photo) => {
|
||||
info!("Created {:?}", photo);
|
||||
|
@ -35,5 +35,6 @@
|
||||
<p>Places: {{#places}}<a href="/place/{{slug}}">{{place_name}}</a>, {{/places}}</p>
|
||||
<p>Tags: {{#tags}}<a href="/tag/{{slug}}">{{tag_name}}</a>, {{/tags}}</p>
|
||||
{{#position}}<p>Position: {{x}} {{y}}</p>{{/position}}
|
||||
{{#camera}}<p>Camera: {{model}} ({{manufacturer}})</p>{{/camera}}
|
||||
</body>
|
||||
</html>
|
||||
|
Loading…
Reference in New Issue
Block a user