diff --git a/backend/middlewares/GalleryMWs.ts b/backend/middlewares/GalleryMWs.ts index be472910..1014cb51 100644 --- a/backend/middlewares/GalleryMWs.ts +++ b/backend/middlewares/GalleryMWs.ts @@ -31,7 +31,7 @@ export class GalleryMWs { } //remove cyclic reference - directory.photos.forEach((photo:Photo) => { + directory.photos.forEach((photo:Photo) => { photo.directory = null; }); diff --git a/backend/model/DiskManger.ts b/backend/model/DiskManger.ts index 534c5ef0..3c7e45ce 100644 --- a/backend/model/DiskManger.ts +++ b/backend/model/DiskManger.ts @@ -1,9 +1,17 @@ import * as fs from "fs"; import * as path from "path"; import * as mime from "mime"; -import * as sizeOf from "image-size"; +import * as iptc from "node-iptc"; +import * as exif_parser from "exif-parser"; import {Directory} from "../../common/entities/Directory"; -import {Photo} from "../../common/entities/Photo"; +import { + Photo, + PhotoMetadata, + ImageSize, + CameraMetadata, + PositionMetaData, + GPSMetadata +} from "../../common/entities/Photo"; export class DiskManager { public static scanDirectory(relativeDirectoryName, cb:(error:any, result:Directory) => void) { @@ -14,6 +22,7 @@ export class DiskManager { let directory = new Directory(1, directoryName, directoryParent, new Date(), [], []); + let promises:Array< Promise > = []; fs.readdir(absoluteDirectoryName, function (err, list) { if (err) { @@ -29,12 +38,19 @@ export class DiskManager { } if (DiskManager.isImage(fullFilePath)) { - let dimensions = sizeOf(fullFilePath); - directory.photos.push(new Photo(1, file, directory, dimensions.width, dimensions.height)); + + + let promise = DiskManager.loadPhotoMetadata(fullFilePath).then((photoMetadata)=> { + directory.photos.push(new Photo(1, file, directory, photoMetadata)); + }); + + promises.push(promise); } } - return cb(err, directory); + Promise.all(promises).then(()=> { + return cb(err, directory); + }); }); } @@ -60,4 +76,51 @@ export class DiskManager { return false; } + + + private static loadPhotoMetadata(fullPath):Promise { + return new Promise((resolve:(metadata:PhotoMetadata)=>void, reject) => { + fs.readFile(fullPath, function (err, data) { + if (err) { + reject(err); + } else { + let exif = exif_parser.create(data).parse(); + let iptcData = iptc(data); + + let imageSize:ImageSize = {width: exif.imageSize.width, height: exif.imageSize.height}; + let cameraData:CameraMetadata = { + ISO: exif.tags.ISO, + model: exif.tags.Modeol, + maker: exif.tags.Make, + fStop: exif.tags.FNumber, + exposure: exif.tags.ExposureTime, + focalLength: exif.tags.FocalLength, + lens: exif.tags.LensModel, + }; + let GPS:GPSMetadata = { + latitude: exif.tags.GPSLatitude, + longitude: exif.tags.GPSLongitude, + altitude: exif.tags.GPSAltitude + + }; + + let positionData:PositionMetaData = { + GPSData: GPS, + country: iptcData.country_or_primary_location_name, + state: iptcData.province_or_state, + city: iptcData.city + }; + + let keywords:[string] = iptcData.keywords; + let creationDate:Date = iptcData.date_time; + + + let metadata:PhotoMetadata = new PhotoMetadata(keywords, cameraData, positionData, imageSize, creationDate); + resolve(metadata); + } + }); + }); + + + } } \ No newline at end of file diff --git a/backend/model/exif.d.ts b/backend/model/exif.d.ts new file mode 100644 index 00000000..2c47310d --- /dev/null +++ b/backend/model/exif.d.ts @@ -0,0 +1,15 @@ +declare module "node-iptc" { + + function e(data):any; + + module e { + } + + export = e; +} + + +declare module "exif-parser" { + export function create(data):any; +} + diff --git a/backend/model/mongoose/MongoGalleryManager.ts b/backend/model/mongoose/MongoGalleryManager.ts index c6e43b96..42137e9a 100644 --- a/backend/model/mongoose/MongoGalleryManager.ts +++ b/backend/model/mongoose/MongoGalleryManager.ts @@ -16,6 +16,7 @@ export class MongoGalleryManager implements IGalleryManager { let directoryName = path.basename(relativeDirectoryName); let directoryParent = path.join(path.dirname(relativeDirectoryName), "/"); + DirectoryModel.findOne({ name: directoryName, path: directoryParent @@ -64,7 +65,7 @@ export class MongoGalleryManager implements IGalleryManager { let directoryEntity = new Directory(directoryModel._id, directoryModel.name, directoryModel.path, directoryModel.lastupdate, [], []); directoryModel.photos.forEach((photo) => { - let photoEntity = new Photo(photo._id, photo.name, null, photo.width, photo.height); + let photoEntity = new Photo(photo._id, photo.name, directoryEntity, photo.metadata); directoryEntity.photos.push(photoEntity); }); diff --git a/backend/model/mongoose/entities/PhotoModel.ts b/backend/model/mongoose/entities/PhotoModel.ts index 9b06abfe..d6bd030a 100644 --- a/backend/model/mongoose/entities/PhotoModel.ts +++ b/backend/model/mongoose/entities/PhotoModel.ts @@ -2,14 +2,38 @@ import {DatabaseManager} from "../DatabaseManager"; import {Schema} from "mongoose"; -export var PhotoModel = DatabaseManager.getInstance().getModel('photo',{ - name:String, - width:Number, - height: Number, +export var PhotoModel = DatabaseManager.getInstance().getModel('photo', { + name: String, directory: { type: Schema.Types.ObjectId, ref: 'directory' }, + metadata: { + keywords: [String], + cameraData: { + ISO: Number, + maker: String, + fStop: Number, + exposure: Number, + focalLength: Number, + lens: String + }, + positionData: { + GPSData: { + latitude: Number, + longitude: Number, + altitude: Number + }, + country: String, + state: String, + city: String + }, + size: { + width: Number, + height: Number + }, + creationDate: Date + } }); \ No newline at end of file diff --git a/backend/routes/GalleryRouter.ts b/backend/routes/GalleryRouter.ts index 2124463e..245f4eb1 100644 --- a/backend/routes/GalleryRouter.ts +++ b/backend/routes/GalleryRouter.ts @@ -19,7 +19,7 @@ export class GalleryRouter { private addDirectoryList() { this.app.get(["/api/gallery/content/:directory(*)", "/api/gallery/", "/api/gallery//"], - AuthenticationMWs.authenticate, + // AuthenticationMWs.authenticate, GalleryMWs.listDirectory, RenderingMWs.renderResult ); diff --git a/common/Utils.ts b/common/Utils.ts index c40d4573..89cbf7f4 100644 --- a/common/Utils.ts +++ b/common/Utils.ts @@ -35,7 +35,20 @@ export class Utils { public static setKeys(targetObject, sourceObject) { Object.keys(sourceObject).forEach((key)=> { if (typeof targetObject[key] === "object") { - Utils.updateKeys(targetObject[key], sourceObject[key]); + Utils.setKeys(targetObject[key], sourceObject[key]); + } else { + targetObject[key] = sourceObject[key]; + } + }); + } + + public static setKeysForced(targetObject, sourceObject) { + Object.keys(sourceObject).forEach((key)=> { + if (typeof sourceObject[key] === "object") { + if (typeof targetObject[key] === "undefined") { + targetObject[key] = {}; + } + Utils.setKeysForced(targetObject[key], sourceObject[key]); } else { targetObject[key] = sourceObject[key]; } diff --git a/common/entities/Photo.ts b/common/entities/Photo.ts index 23114d51..de3e7962 100644 --- a/common/entities/Photo.ts +++ b/common/entities/Photo.ts @@ -1,14 +1,47 @@ import {Directory} from "./Directory"; export class Photo { - constructor(public id?:number, public name?:string, public directory?:Directory, public width?:number, public height?:number) { + constructor(public id?:number, + public name?:string, + public directory?:Directory, + public metadata?:PhotoMetadata) { } +} - /*public static getThumbnailPath(photo:Photo) { - return Utils.concatUrls("/api/gallery/content/", photo.directory.path, photo.directory.name, photo.name, "thumbnail"); +export class PhotoMetadata { + constructor(public keywords?:Array, + public cameraData?:CameraMetadata, + public positionData?:PositionMetaData, + public size?:ImageSize, + public creationDate?:Date) { } +} + +export interface ImageSize { + width:number; + height:number; +} + +export interface CameraMetadata { + ISO?:number; + model?:string; + maker?:string; + fStop?:number; + exposure?:number; + focalLength?:number; + lens?:string; +} + +export interface PositionMetaData { + GPSData?:GPSMetadata; + country?:string; + state?:string; + city?:string; +} + +export interface GPSMetadata { + latitude?:string; + longitude?:string; + altitude?:string; - public static getPhotoPath(photo:Photo) { - return Utils.concatUrls("/api/gallery/content/", photo.directory.path, photo.directory.name, photo.name); - }*/ } \ No newline at end of file diff --git a/demo/images/subdirectory/IMG_8808.jpg b/demo/images/subdirectory/IMG_8808.jpg deleted file mode 100644 index 51d0c367..00000000 Binary files a/demo/images/subdirectory/IMG_8808.jpg and /dev/null differ diff --git a/frontend/app/gallery/grid/GridRowBuilder.ts b/frontend/app/gallery/grid/GridRowBuilder.ts index c947d73f..fe17005f 100644 --- a/frontend/app/gallery/grid/GridRowBuilder.ts +++ b/frontend/app/gallery/grid/GridRowBuilder.ts @@ -55,7 +55,7 @@ export class GridRowBuilder { public calcRowHeight():number { let width = 0; for (let i = 0; i < this.photoRow.length; i++) { - width += ((this.photoRow[i].width) / (this.photoRow[i].height)); //summing up aspect ratios + width += ((this.photoRow[i].metadata.size.width) / (this.photoRow[i].metadata.size.height)); //summing up aspect ratios } let height = (this.containerWidth - this.photoRow.length * (this.photoMargin * 2) - 1) / width; //cant be equal -> width-1 diff --git a/frontend/app/gallery/grid/grid.gallery.component.ts b/frontend/app/gallery/grid/grid.gallery.component.ts index 68a1fdca..78ba753f 100644 --- a/frontend/app/gallery/grid/grid.gallery.component.ts +++ b/frontend/app/gallery/grid/grid.gallery.component.ts @@ -104,7 +104,7 @@ export class GalleryGridComponent implements OnChanges,AfterViewInit { let imageHeight = rowHeight - (this.IMAGE_MARGIN * 2); photoRowBuilder.getPhotoRow().forEach((photo) => { - let imageWidth = imageHeight * (photo.width / photo.height); + let imageWidth = imageHeight * (photo.metadata.size.width / photo.metadata.size.height); this.photosToRender.push(new GridPhoto(photo, imageWidth, imageHeight)); }); diff --git a/frontend/app/gallery/lightbox/lightbox.gallery.component.ts b/frontend/app/gallery/lightbox/lightbox.gallery.component.ts index 7720cb3c..6d8cb96e 100644 --- a/frontend/app/gallery/lightbox/lightbox.gallery.component.ts +++ b/frontend/app/gallery/lightbox/lightbox.gallery.component.ts @@ -176,12 +176,12 @@ export class GalleryLightboxComponent { private calcLightBoxPhotoDimension(photo:Photo):Dimension { let width = 0; let height = 0; - if (photo.height > photo.width) { - width = Math.round(photo.width * (this.getScreenHeight() / photo.height)); + if (photo.metadata.size.height > photo.metadata.size.width) { + width = Math.round(photo.metadata.size.width * (this.getScreenHeight() / photo.metadata.size.height)); height = this.getScreenHeight(); } else { width = this.getScreenWidth(); - height = Math.round(photo.height * (this.getScreenWidth() / photo.width)); + height = Math.round(photo.metadata.size.height * (this.getScreenWidth() / photo.metadata.size.width)); } let top = (this.getScreenHeight() / 2 - height / 2); let left = (this.getScreenWidth() / 2 - width / 2); diff --git a/package.json b/package.json index 2cf630e2..51024ed1 100644 --- a/package.json +++ b/package.json @@ -35,14 +35,15 @@ "core-js": "^2.4.0", "debug": "^2.2.0", "ejs": "^2.4.1", + "exif-parser": "^0.1.9", "express": "^4.13.4", "express-session": "^1.13.0", - "image-size": "^0.5.0", "jimp": "^0.2.24", "mime": "^1.3.4", "mongoose": "^4.4.16", "morgan": "^1.7.0", "ng2-cookies": "^0.1.5", + "node-iptc": "^1.0.4", "optimist": "^0.6.1", "rxjs": "5.0.0-beta.6", "ts-loader": "^0.8.2", diff --git a/typings.json b/typings.json index 691ccd54..b7cb22ba 100644 --- a/typings.json +++ b/typings.json @@ -7,7 +7,6 @@ "debug": "github:DefinitelyTyped/DefinitelyTyped/debug/debug.d.ts#0d622d857f97d44ea7dcad2b3edec1f23c48fe9e", "express": "github:DefinitelyTyped/DefinitelyTyped/express/express.d.ts#0d622d857f97d44ea7dcad2b3edec1f23c48fe9e", "express-session": "registry:dt/express-session#0.0.0+20160331200931", - "image-size": "registry:dt/image-size#0.0.0+20160223165602", "jasmine": "github:DefinitelyTyped/DefinitelyTyped/jasmine/jasmine.d.ts#d22516f9f089de107d7e7d5938566377370631f6", "mime": "github:DefinitelyTyped/DefinitelyTyped/mime/mime.d.ts#0d622d857f97d44ea7dcad2b3edec1f23c48fe9e", "mongoose": "registry:dt/mongoose#3.8.5+20160316155526",