From c66a9b49f3e9e7322ca63b29458f5320441b0556 Mon Sep 17 00:00:00 2001 From: Braun Patrik Date: Thu, 12 May 2016 11:00:46 +0200 Subject: [PATCH] implementing thumbnail sizes --- backend/config/Config.ts | 1 - backend/middlewares/ThumbnailGeneratorMWs.ts | 31 +++++++++++----- backend/middlewares/jimp.d.ts | 5 +++ backend/server.ts | 6 ++-- common/Utils.ts | 22 +++++++++++- common/config/Config.ts | 3 +- common/entities/Photo.ts | 6 ++-- frontend/app/gallery/grid/GridPhoto.ts | 18 ++++++++++ .../gallery/grid/grid.gallery.component.css | 3 +- .../gallery/grid/grid.gallery.component.html | 18 +++++----- .../gallery/grid/grid.gallery.component.ts | 9 +++-- .../photo/photo.grid.gallery.component.css} | 0 .../photo/photo.grid.gallery.component.html | 1 + .../photo/photo.grid.gallery.component.ts | 35 +++++++++++++++++++ .../lightbox/lightbox.gallery.component.ts | 12 +++---- .../photo/photo.gallery.component.html | 1 - .../gallery/photo/photo.gallery.component.ts | 32 ----------------- package.json | 15 ++++---- typings.json | 1 - 19 files changed, 138 insertions(+), 81 deletions(-) create mode 100644 frontend/app/gallery/grid/GridPhoto.ts rename frontend/app/gallery/{photo/photo.gallery.component.css => grid/photo/photo.grid.gallery.component.css} (100%) create mode 100644 frontend/app/gallery/grid/photo/photo.grid.gallery.component.html create mode 100644 frontend/app/gallery/grid/photo/photo.grid.gallery.component.ts delete mode 100644 frontend/app/gallery/photo/photo.gallery.component.html delete mode 100644 frontend/app/gallery/photo/photo.gallery.component.ts diff --git a/backend/config/Config.ts b/backend/config/Config.ts index d154e55f..ab795094 100644 --- a/backend/config/Config.ts +++ b/backend/config/Config.ts @@ -8,7 +8,6 @@ export var Config = new ConfigClass(); Config.Server = { port: 80, - thumbnailSizes: [200], imagesFolder: "/demo/images", thumbnailFolder: "/demo/TEMP", databaseType: DatabaseType.mongoDB diff --git a/backend/middlewares/ThumbnailGeneratorMWs.ts b/backend/middlewares/ThumbnailGeneratorMWs.ts index ea4dc536..64e87160 100644 --- a/backend/middlewares/ThumbnailGeneratorMWs.ts +++ b/backend/middlewares/ThumbnailGeneratorMWs.ts @@ -20,33 +20,47 @@ export class ThumbnailGeneratorMWs { if (!req.resultPipe) return next(); + //load parameters let imagePath = req.resultPipe; - let size:number = parseInt(req.params.size) || Config.Server.thumbnailSizes[0]; + let size:number = parseInt(req.params.size) || Config.Client.thumbnailSizes[0]; let thumbnailFolder = ThumbnailGeneratorMWs.getThumbnailFolder(); - if (Config.Server.thumbnailSizes.indexOf(size) === -1) { - size = Config.Server.thumbnailSizes[0]; + //validate size + if (Config.Client.thumbnailSizes.indexOf(size) === -1) { + size = Config.Client.thumbnailSizes[0]; } + //generate thumbnail path let thPath = path.join(thumbnailFolder, ThumbnailGeneratorMWs.generateThumbnailName(imagePath, size)); req.resultPipe = thPath; + //check if thumbnail already exist if (fs.existsSync(thPath) === true) { return next(); } + //create thumbnail folder if not exist if (!fs.existsSync(thumbnailFolder)) { fs.mkdirSync(thumbnailFolder); } + //generate thumbnail Jimp.read(imagePath).then((image) => { - if (image.bitmap.with < image.bitmap.height) { - image.resize(size, Jimp.AUTO); // resize - } else { - image.resize(Jimp.AUTO, size); // resize - } + /** + * newWidth * newHeight = size*size + * newHeight/newWidth = height/width + * + * newHeight = (height/width)*newWidth + * newWidth * newWidth = (size*size) / (height/width) + * + * @type {number} + */ + let ratio = image.bitmap.height / image.bitmap.width; + let newWidth = Math.sqrt((size * size) / ratio); + + image.resize(newWidth, Jimp.AUTO, Jimp.RESIZE_BEZIER); image.quality(60); // set JPEG quality image.write(thPath, () => { // save @@ -55,6 +69,7 @@ export class ThumbnailGeneratorMWs { }).catch(function (err) { return next(new Error(ErrorCodes.GENERAL_ERROR)); }); + } private static generateThumbnailName(imagePath:string, size:number):string { diff --git a/backend/middlewares/jimp.d.ts b/backend/middlewares/jimp.d.ts index 6b9b359a..854571f2 100644 --- a/backend/middlewares/jimp.d.ts +++ b/backend/middlewares/jimp.d.ts @@ -1,5 +1,10 @@ declare module "jimp" { function read(filaname); + var RESIZE_NEAREST_NEIGHBOR; + var RESIZE_BILINEAR; + var RESIZE_BICUBIC; + var RESIZE_HERMITE; + var RESIZE_BEZIER; var AUTO:any; } \ No newline at end of file diff --git a/backend/server.ts b/backend/server.ts index 266581ce..90951b06 100644 --- a/backend/server.ts +++ b/backend/server.ts @@ -3,7 +3,6 @@ import * as _express from "express"; import * as _session from "express-session"; import * as _bodyParser from "body-parser"; -import * as _compress from "compression"; import * as _debug from "debug"; import * as _http from "http"; import {PublicRouter} from "./routes/PublicRouter"; @@ -29,6 +28,7 @@ export class Server { this.debug = _debug("PiGallery2:server"); this.app = _express(); + this.app.set('view engine', 'ejs'); if (process.env.DEBUG) { @@ -36,9 +36,7 @@ export class Server { this.app.use(_morgan('dev')); } - //enable gzip - this.app.use(_compress()); - + /** * Session above all */ diff --git a/common/Utils.ts b/common/Utils.ts index 73800e92..c40d4573 100644 --- a/common/Utils.ts +++ b/common/Utils.ts @@ -20,7 +20,7 @@ export class Utils { } public static updateKeys(targetObject, sourceObject) { - Object.keys(sourceObject).forEach((key)=> { + Object.keys(sourceObject).forEach((key)=> { if (typeof targetObject[key] === "undefined") { return; } @@ -56,4 +56,24 @@ export class Utils { return arr; } + + public static findClosest(number:number, arr:Array) { + + let curr = arr[0]; + let diff = Math.abs(number - curr); + + arr.forEach((value)=> { + + let newDiff = Math.abs(number - value); + + if (newDiff < diff) { + diff = newDiff; + curr = value; + } + + }); + + return curr; + } + } diff --git a/common/config/Config.ts b/common/config/Config.ts index 8645d56a..ded6c71d 100644 --- a/common/config/Config.ts +++ b/common/config/Config.ts @@ -4,7 +4,6 @@ export enum DatabaseType{ interface ServerConfig { port:number; - thumbnailSizes:Array; imagesFolder:string; thumbnailFolder:string; databaseType:DatabaseType; @@ -17,6 +16,7 @@ interface SearchConfig { } interface ClientConfig { + thumbnailSizes:Array; Search:SearchConfig; } export class ConfigClass { @@ -24,6 +24,7 @@ export class ConfigClass { public Server:ServerConfig = null; public Client:ClientConfig = { + thumbnailSizes: [200, 400, 600], Search: { searchEnabled: true, instantSearchEnabled: true, diff --git a/common/entities/Photo.ts b/common/entities/Photo.ts index 329c78f7..23114d51 100644 --- a/common/entities/Photo.ts +++ b/common/entities/Photo.ts @@ -1,14 +1,14 @@ -import {Utils} from "../Utils"; import {Directory} from "./Directory"; + export class Photo { constructor(public id?:number, public name?:string, public directory?:Directory, public width?:number, public height?:number) { } - public static getThumbnailPath(photo:Photo) { + /*public static getThumbnailPath(photo:Photo) { return Utils.concatUrls("/api/gallery/content/", photo.directory.path, photo.directory.name, photo.name, "thumbnail"); } 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/frontend/app/gallery/grid/GridPhoto.ts b/frontend/app/gallery/grid/GridPhoto.ts new file mode 100644 index 00000000..1a87b742 --- /dev/null +++ b/frontend/app/gallery/grid/GridPhoto.ts @@ -0,0 +1,18 @@ +import {Photo} from "../../../../common/entities/Photo"; +import {Config} from "../../config/Config"; +import {Utils} from "../../../../common/Utils"; +export class GridPhoto { + constructor(public photo:Photo, public renderWidth:number, public renderHeight:number) { + + } + + getThumbnailPath() { + let renderSize = Math.sqrt(this.renderWidth * this.renderHeight); + let size = Utils.findClosest(renderSize, Config.Client.thumbnailSizes); + return Utils.concatUrls("/api/gallery/content/", this.photo.directory.path, this.photo.directory.name, this.photo.name, "thumbnail", size.toString()); + } + + getPhotoPath() { + return Utils.concatUrls("/api/gallery/content/", this.photo.directory.path, this.photo.directory.name, this.photo.name); + } +} \ No newline at end of file diff --git a/frontend/app/gallery/grid/grid.gallery.component.css b/frontend/app/gallery/grid/grid.gallery.component.css index 0baebea2..a11f2d86 100644 --- a/frontend/app/gallery/grid/grid.gallery.component.css +++ b/frontend/app/gallery/grid/grid.gallery.component.css @@ -3,7 +3,8 @@ div { line-height: normal; font-size: 0; } -gallery-photo { + +gallery-grid-photo { display: inline-block; cursor: pointer; diff --git a/frontend/app/gallery/grid/grid.gallery.component.html b/frontend/app/gallery/grid/grid.gallery.component.html index 04998a8a..018abadd 100644 --- a/frontend/app/gallery/grid/grid.gallery.component.html +++ b/frontend/app/gallery/grid/grid.gallery.component.html @@ -1,12 +1,12 @@
- + - +
\ No newline at end of file diff --git a/frontend/app/gallery/grid/grid.gallery.component.ts b/frontend/app/gallery/grid/grid.gallery.component.ts index 92ba7dbd..68a1fdca 100644 --- a/frontend/app/gallery/grid/grid.gallery.component.ts +++ b/frontend/app/gallery/grid/grid.gallery.component.ts @@ -11,9 +11,10 @@ import { AfterViewInit } from "@angular/core"; import {Photo} from "../../../../common/entities/Photo"; -import {GalleryPhotoComponent} from "../photo/photo.gallery.component"; import {GridRowBuilder} from "./GridRowBuilder"; import {GalleryLightboxComponent} from "../lightbox/lightbox.gallery.component"; +import {GridPhoto} from "./GridPhoto"; +import {GalleryPhotoComponent} from "./photo/photo.grid.gallery.component"; @Component({ selector: 'gallery-grid', @@ -49,6 +50,8 @@ export class GalleryGridComponent implements OnChanges,AfterViewInit { ngAfterViewInit() { this.lightbox.gridPhotoQL = this.gridPhotoQL; + + //TODO: implement scroll detection /* this.gridPhotoQL.changes.subscribe( (x)=> { console.log("changed"); @@ -121,8 +124,4 @@ export class GalleryGridComponent implements OnChanges,AfterViewInit { } -class GridPhoto { - constructor(public photo:Photo, public renderWidth:number, public renderHeight:number) { - } -} diff --git a/frontend/app/gallery/photo/photo.gallery.component.css b/frontend/app/gallery/grid/photo/photo.grid.gallery.component.css similarity index 100% rename from frontend/app/gallery/photo/photo.gallery.component.css rename to frontend/app/gallery/grid/photo/photo.grid.gallery.component.css diff --git a/frontend/app/gallery/grid/photo/photo.grid.gallery.component.html b/frontend/app/gallery/grid/photo/photo.grid.gallery.component.html new file mode 100644 index 00000000..5cbbfd53 --- /dev/null +++ b/frontend/app/gallery/grid/photo/photo.grid.gallery.component.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/app/gallery/grid/photo/photo.grid.gallery.component.ts b/frontend/app/gallery/grid/photo/photo.grid.gallery.component.ts new file mode 100644 index 00000000..bf55fb35 --- /dev/null +++ b/frontend/app/gallery/grid/photo/photo.grid.gallery.component.ts @@ -0,0 +1,35 @@ +/// + +import {Component, Input, ElementRef, ViewChild} from "@angular/core"; +import {IRenderable, Dimension} from "../../../model/IRenderable"; +import {GridPhoto} from "../GridPhoto"; + +@Component({ + selector: 'gallery-grid-photo', + templateUrl: 'app/gallery/grid/photo/photo.grid.gallery.component.html', + styleUrls: ['app/gallery/grid/photo/photo.grid.gallery.component.css'], +}) +export class GalleryPhotoComponent implements IRenderable { + @Input() gridPhoto:GridPhoto; + @ViewChild("image") imageRef:ElementRef; + + constructor() { + } + + /* getPhotoPath() { + + let renderSize = Math.sqrt(this.gridPhoto.renderWidth * this.gridPhoto.renderHeight); + let size = Utils.findClosest(renderSize, Config.Client.thumbnailSizes); + return Utils.concatUrls("/api/gallery/content/", this.gridPhoto.photo.directory.path, this.gridPhoto.photo.directory.name, this.gridPhoto.photo.name, "thumbnail", size.toString()); + } + */ + + public getDimension():Dimension { + return new Dimension(this.imageRef.nativeElement.offsetTop, + this.imageRef.nativeElement.offsetLeft, + this.imageRef.nativeElement.width, + this.imageRef.nativeElement.height); + } + +} + diff --git a/frontend/app/gallery/lightbox/lightbox.gallery.component.ts b/frontend/app/gallery/lightbox/lightbox.gallery.component.ts index 3ac24fbe..7720cb3c 100644 --- a/frontend/app/gallery/lightbox/lightbox.gallery.component.ts +++ b/frontend/app/gallery/lightbox/lightbox.gallery.component.ts @@ -2,7 +2,7 @@ import {Component, ElementRef, ViewChild, QueryList} from "@angular/core"; import {Photo} from "../../../../common/entities/Photo"; -import {GalleryPhotoComponent} from "../photo/photo.gallery.component"; +import {GalleryPhotoComponent} from "../grid/photo/photo.grid.gallery.component.ts"; import {AnimationBuilder} from "@angular/platform-browser/src/animate/animation_builder"; import {BrowserDomAdapter} from "@angular/platform-browser/src/browser_common"; import {Dimension} from "../../model/IRenderable"; @@ -46,7 +46,7 @@ export class GalleryLightboxComponent { let fromImage = {width: from.width + "px", height: from.height + "px", top: "0px", left: "0px"}; - let toImage = this.calcLightBoxPhotoDimension(this.activePhoto.photo).toStyle(); + let toImage = this.calcLightBoxPhotoDimension(this.activePhoto.gridPhoto.photo).toStyle(); this.forceAnimateFrom(fromImage, toImage, @@ -89,7 +89,7 @@ export class GalleryLightboxComponent { {display: "none"}); - let fromImage = this.calcLightBoxPhotoDimension(this.activePhoto.photo).toStyle(); + let fromImage = this.calcLightBoxPhotoDimension(this.activePhoto.gridPhoto.photo).toStyle(); let toImage = {width: to.width + "px", height: to.height + "px", top: "0px", left: "0px"}; this.forceAnimateTo(fromImage, @@ -106,7 +106,7 @@ export class GalleryLightboxComponent { let galleryPhotoComponents = this.gridPhotoQL.toArray(); let selectedPhoto:GalleryPhotoComponent = null; for (let i = 0; i < galleryPhotoComponents.length; i++) { - if (galleryPhotoComponents[i].photo == photo) { + if (galleryPhotoComponents[i].gridPhoto.photo == photo) { selectedPhoto = galleryPhotoComponents[i]; break; } @@ -150,14 +150,14 @@ export class GalleryLightboxComponent { if (!this.activePhoto) { return ""; } - return Photo.getPhotoPath(this.activePhoto.photo); + return this.activePhoto.gridPhoto.getPhotoPath(); } getThumbnailPath() { if (!this.activePhoto) { return ""; } - return Photo.getThumbnailPath(this.activePhoto.photo); + return this.activePhoto.gridPhoto.getThumbnailPath(); } private getBodyScrollTop() { diff --git a/frontend/app/gallery/photo/photo.gallery.component.html b/frontend/app/gallery/photo/photo.gallery.component.html deleted file mode 100644 index 4fd40261..00000000 --- a/frontend/app/gallery/photo/photo.gallery.component.html +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/frontend/app/gallery/photo/photo.gallery.component.ts b/frontend/app/gallery/photo/photo.gallery.component.ts deleted file mode 100644 index 699f6531..00000000 --- a/frontend/app/gallery/photo/photo.gallery.component.ts +++ /dev/null @@ -1,32 +0,0 @@ -/// - -import {Component, Input, ElementRef, ViewChild} from "@angular/core"; -import {Photo} from "../../../../common/entities/Photo"; -import {IRenderable, Dimension} from "../../model/IRenderable"; - -@Component({ - selector: 'gallery-photo', - templateUrl: 'app/gallery/photo/photo.gallery.component.html', - styleUrls: ['app/gallery/photo/photo.gallery.component.css'], -}) -export class GalleryPhotoComponent implements IRenderable { - @Input() photo:Photo; - @ViewChild("image") imageRef:ElementRef; - - constructor() { - } - - getPhotoPath() { - return Photo.getThumbnailPath(this.photo); - } - - - public getDimension():Dimension { - return new Dimension(this.imageRef.nativeElement.offsetTop, - this.imageRef.nativeElement.offsetLeft, - this.imageRef.nativeElement.width, - this.imageRef.nativeElement.height); - } - -} - diff --git a/package.json b/package.json index af51407f..2cf630e2 100644 --- a/package.json +++ b/package.json @@ -31,9 +31,8 @@ "@angular/platform-server": "2.0.0-rc.1", "@angular/router": "2.0.0-rc.1", "@angular/router-deprecated": "2.0.0-rc.1", - "body-parser": "^1.15.0", - "compression": "^1.6.1", - "core-js": "^2.3.0", + "body-parser": "^1.15.1", + "core-js": "^2.4.0", "debug": "^2.2.0", "ejs": "^2.4.1", "express": "^4.13.4", @@ -41,13 +40,13 @@ "image-size": "^0.5.0", "jimp": "^0.2.24", "mime": "^1.3.4", - "mongoose": "^4.4.14", + "mongoose": "^4.4.16", "morgan": "^1.7.0", "ng2-cookies": "^0.1.5", "optimist": "^0.6.1", "rxjs": "5.0.0-beta.6", "ts-loader": "^0.8.2", - "tslint": "^3.9.0", + "tslint": "^3.10.1", "typescript": "^1.8.10", "typings": "^0.8.1", "webpack": "^1.13.0", @@ -67,10 +66,10 @@ "jasmine-core": "^2.4.1", "json-loader": "^0.5.4", "karma": "^0.13.21", - "karma-coverage": "^0.5.3", + "karma-coverage": "^1.0.0", "karma-coveralls": "^1.1.2", - "karma-jasmine": "^0.3.7", - "karma-mocha-reporter": "^2.0.0", + "karma-jasmine": "^1.0.2", + "karma-mocha-reporter": "^2.0.3", "karma-phantomjs-launcher": "^1.0.0", "karma-sourcemap-loader": "^0.3.7", "karma-webpack": "1.7.0", diff --git a/typings.json b/typings.json index 94f4cd66..691ccd54 100644 --- a/typings.json +++ b/typings.json @@ -3,7 +3,6 @@ "version": false, "ambientDependencies": { "body-parser": "registry:dt/body-parser#0.0.0+20160317120654", - "compression": "registry:dt/compression#0.0.0+20160501162003", "core-js": "registry:dt/core-js#0.0.0+20160317120654", "debug": "github:DefinitelyTyped/DefinitelyTyped/debug/debug.d.ts#0d622d857f97d44ea7dcad2b3edec1f23c48fe9e", "express": "github:DefinitelyTyped/DefinitelyTyped/express/express.d.ts#0d622d857f97d44ea7dcad2b3edec1f23c48fe9e",