improving thumbnail generation. adding thumbnails to map
This commit is contained in:
parent
483af01c99
commit
385dcd7c4d
@ -73,10 +73,10 @@ export class ThumbnailGeneratorMWs {
|
|||||||
|
|
||||||
private static addThInfoToPhotos(photos: Array<PhotoDTO>) {
|
private static addThInfoToPhotos(photos: Array<PhotoDTO>) {
|
||||||
let thumbnailFolder = ProjectPath.ThumbnailFolder;
|
let thumbnailFolder = ProjectPath.ThumbnailFolder;
|
||||||
for (let j = 0; j < Config.Client.thumbnailSizes.length; j++) {
|
for (let i = 0; i < photos.length; i++) {
|
||||||
let size = Config.Client.thumbnailSizes[j];
|
let fullImagePath = path.join(ProjectPath.ImageFolder, photos[i].directory.path, photos[i].directory.name, photos[i].name);
|
||||||
for (let i = 0; i < photos.length; i++) {
|
for (let j = 0; j < Config.Client.thumbnailSizes.length; j++) {
|
||||||
let fullImagePath = path.join(ProjectPath.ImageFolder, photos[i].directory.path, photos[i].directory.name, photos[i].name);
|
let size = Config.Client.thumbnailSizes[j];
|
||||||
let thPath = path.join(thumbnailFolder, ThumbnailGeneratorMWs.generateThumbnailName(fullImagePath, size));
|
let thPath = path.join(thumbnailFolder, ThumbnailGeneratorMWs.generateThumbnailName(fullImagePath, size));
|
||||||
if (fs.existsSync(thPath) === true) {
|
if (fs.existsSync(thPath) === true) {
|
||||||
if (typeof photos[i].readyThumbnails == "undefined") {
|
if (typeof photos[i].readyThumbnails == "undefined") {
|
||||||
@ -85,6 +85,11 @@ export class ThumbnailGeneratorMWs {
|
|||||||
photos[i].readyThumbnails.push(size);
|
photos[i].readyThumbnails.push(size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let iconPath = path.join(thumbnailFolder, ThumbnailGeneratorMWs.generateThumbnailName(fullImagePath, Config.Client.iconSize));
|
||||||
|
if (fs.existsSync(iconPath) === true) {
|
||||||
|
photos[i].readyIcon = true;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,13 +134,13 @@ export class ThumbnailGeneratorMWs {
|
|||||||
|
|
||||||
//load parameters
|
//load parameters
|
||||||
let imagePath = req.resultPipe;
|
let imagePath = req.resultPipe;
|
||||||
let size: number = 30;
|
let size: number = Config.Client.iconSize;
|
||||||
ThumbnailGeneratorMWs.generateImage(imagePath, size, true, req, res, next);
|
ThumbnailGeneratorMWs.generateImage(imagePath, size, true, req, res, next);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static generateImage(imagePath, size, makeSquare, req: Request, res: Response, next: NextFunction) {
|
private static generateImage(imagePath: string, size: number, makeSquare: boolean, req: Request, res: Response, next: NextFunction) {
|
||||||
|
|
||||||
//generate thumbnail path
|
//generate thumbnail path
|
||||||
let thPath = path.join(ProjectPath.ThumbnailFolder, ThumbnailGeneratorMWs.generateThumbnailName(imagePath, size));
|
let thPath = path.join(ProjectPath.ThumbnailFolder, ThumbnailGeneratorMWs.generateThumbnailName(imagePath, size));
|
||||||
|
|||||||
@ -39,6 +39,7 @@ export class GalleryManager implements IGalleryManager {
|
|||||||
dir.photos[i].metadata.positionData = <any>JSON.parse(<any>dir.photos[i].metadata.positionData);
|
dir.photos[i].metadata.positionData = <any>JSON.parse(<any>dir.photos[i].metadata.positionData);
|
||||||
dir.photos[i].metadata.size = <any>JSON.parse(<any>dir.photos[i].metadata.size);
|
dir.photos[i].metadata.size = <any>JSON.parse(<any>dir.photos[i].metadata.size);
|
||||||
dir.photos[i].readyThumbnails = [];
|
dir.photos[i].readyThumbnails = [];
|
||||||
|
dir.photos[i].readyIcon = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -26,6 +26,8 @@ export class PhotoEntity implements PhotoDTO {
|
|||||||
|
|
||||||
readyThumbnails: Array<number> = [];
|
readyThumbnails: Array<number> = [];
|
||||||
|
|
||||||
|
readyIcon: boolean = false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -14,34 +14,36 @@ interface DataBaseConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface ServerConfig {
|
interface ServerConfig {
|
||||||
port:number;
|
port: number;
|
||||||
imagesFolder:string;
|
imagesFolder: string;
|
||||||
thumbnailFolder:string;
|
thumbnailFolder: string;
|
||||||
database: DataBaseConfig;
|
database: DataBaseConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SearchConfig {
|
interface SearchConfig {
|
||||||
searchEnabled:boolean
|
searchEnabled: boolean
|
||||||
instantSearchEnabled:boolean
|
instantSearchEnabled: boolean
|
||||||
autocompleteEnabled:boolean
|
autocompleteEnabled: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ClientConfig {
|
interface ClientConfig {
|
||||||
thumbnailSizes:Array<number>;
|
iconSize: number;
|
||||||
Search:SearchConfig;
|
thumbnailSizes: Array<number>;
|
||||||
concurrentThumbnailGenerations:number;
|
Search: SearchConfig;
|
||||||
enableCache:boolean;
|
concurrentThumbnailGenerations: number;
|
||||||
enableOnScrollRendering:boolean;
|
enableCache: boolean;
|
||||||
enableOnScrollThumbnailPrioritising:boolean;
|
enableOnScrollRendering: boolean;
|
||||||
authenticationRequired:boolean;
|
enableOnScrollThumbnailPrioritising: boolean;
|
||||||
|
authenticationRequired: boolean;
|
||||||
googleApiKey: string;
|
googleApiKey: string;
|
||||||
}
|
}
|
||||||
export class ConfigClass {
|
export class ConfigClass {
|
||||||
|
|
||||||
public Server:ServerConfig = null;
|
public Server: ServerConfig = null;
|
||||||
|
|
||||||
public Client:ClientConfig = {
|
public Client: ClientConfig = {
|
||||||
thumbnailSizes: [200, 400, 600],
|
thumbnailSizes: [200, 400, 600],
|
||||||
|
iconSize: 30,
|
||||||
Search: {
|
Search: {
|
||||||
searchEnabled: true,
|
searchEnabled: true,
|
||||||
instantSearchEnabled: true,
|
instantSearchEnabled: true,
|
||||||
@ -55,7 +57,7 @@ export class ConfigClass {
|
|||||||
googleApiKey: ""
|
googleApiKey: ""
|
||||||
};
|
};
|
||||||
|
|
||||||
public setDatabaseType(type:DatabaseType) {
|
public setDatabaseType(type: DatabaseType) {
|
||||||
this.Server.database.type = type;
|
this.Server.database.type = type;
|
||||||
if (type === DatabaseType.memory) {
|
if (type === DatabaseType.memory) {
|
||||||
this.Client.Search.searchEnabled = false;
|
this.Client.Search.searchEnabled = false;
|
||||||
|
|||||||
@ -6,6 +6,7 @@ export interface PhotoDTO {
|
|||||||
directory: DirectoryDTO;
|
directory: DirectoryDTO;
|
||||||
metadata: PhotoMetadata;
|
metadata: PhotoMetadata;
|
||||||
readyThumbnails: Array<number>;
|
readyThumbnails: Array<number>;
|
||||||
|
readyIcon: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PhotoMetadata {
|
export interface PhotoMetadata {
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import {appRoutes} from "./app.routing";
|
|||||||
import {UserService} from "./model/network/user.service";
|
import {UserService} from "./model/network/user.service";
|
||||||
import {GalleryService} from "./gallery/gallery.service";
|
import {GalleryService} from "./gallery/gallery.service";
|
||||||
import {NetworkService} from "./model/network/network.service";
|
import {NetworkService} from "./model/network/network.service";
|
||||||
import {ThumbnailLoaderService} from "./gallery/grid/thumnailLoader.service";
|
import {ThumbnailLoaderService} from "./gallery/thumnailLoader.service";
|
||||||
import {GalleryCacheService} from "./gallery/cache.gallery.service";
|
import {GalleryCacheService} from "./gallery/cache.gallery.service";
|
||||||
import {FullScreenService} from "./gallery/fullscreen.service";
|
import {FullScreenService} from "./gallery/fullscreen.service";
|
||||||
import {AuthenticationService} from "./model/network/authentication.service";
|
import {AuthenticationService} from "./model/network/authentication.service";
|
||||||
@ -29,7 +29,7 @@ import {StringifyRole} from "./pipes/StringifyRolePipe";
|
|||||||
import {Config} from "./config/Config";
|
import {Config} from "./config/Config";
|
||||||
import {GalleryMapComponent} from "./gallery/map/map.gallery.component";
|
import {GalleryMapComponent} from "./gallery/map/map.gallery.component";
|
||||||
import {GalleryMapLightboxComponent} from "./gallery/map/lightbox/lightbox.map.gallery.component";
|
import {GalleryMapLightboxComponent} from "./gallery/map/lightbox/lightbox.map.gallery.component";
|
||||||
import {ThumbnailManagerService} from "./gallery/grid/thumnailManager.service";
|
import {ThumbnailManagerService} from "./gallery/thumnailManager.service";
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
|
|||||||
42
frontend/app/gallery/IconPhoto.ts
Normal file
42
frontend/app/gallery/IconPhoto.ts
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import {PhotoDTO} from "../../../common/entities/PhotoDTO";
|
||||||
|
import {Utils} from "../../../common/Utils";
|
||||||
|
export class IconPhoto {
|
||||||
|
|
||||||
|
|
||||||
|
protected replacementSizeCache: number|boolean = false;
|
||||||
|
|
||||||
|
constructor(public photo: PhotoDTO) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
iconLoaded() {
|
||||||
|
this.photo.readyIcon = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
isIconAvailable() {
|
||||||
|
return this.photo.readyIcon;
|
||||||
|
}
|
||||||
|
|
||||||
|
getIconPath() {
|
||||||
|
return Utils.concatUrls("/api/gallery/content/", this.photo.directory.path, this.photo.directory.name, this.photo.name, "icon");
|
||||||
|
}
|
||||||
|
|
||||||
|
getPhotoPath() {
|
||||||
|
return Utils.concatUrls("/api/gallery/content/", this.photo.directory.path, this.photo.directory.name, this.photo.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
equals(other: PhotoDTO | IconPhoto): boolean {
|
||||||
|
//is gridphoto
|
||||||
|
if (other instanceof IconPhoto) {
|
||||||
|
return this.photo.directory.path === other.photo.directory.path && this.photo.directory.name === other.photo.directory.name && this.photo.name === other.photo.name
|
||||||
|
}
|
||||||
|
|
||||||
|
//is photo
|
||||||
|
if (other.directory) {
|
||||||
|
return this.photo.directory.path === other.directory.path && this.photo.directory.name === other.directory.name && this.photo.name === other.name
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,12 +1,12 @@
|
|||||||
import {PhotoDTO} from "../../../common/entities/PhotoDTO";
|
import {PhotoDTO} from "../../../common/entities/PhotoDTO";
|
||||||
import {Utils} from "../../../common/Utils";
|
import {Utils} from "../../../common/Utils";
|
||||||
import {Config} from "../config/Config";
|
import {Config} from "../config/Config";
|
||||||
export class Photo {
|
import {IconPhoto} from "./IconPhoto";
|
||||||
|
export class Photo extends IconPhoto {
|
||||||
|
|
||||||
protected replacementSizeCache: boolean|number = false;
|
|
||||||
|
|
||||||
constructor(public photo: PhotoDTO, public renderWidth: number, public renderHeight: number) {
|
|
||||||
|
|
||||||
|
constructor(photo: PhotoDTO, public renderWidth: number, public renderHeight: number) {
|
||||||
|
super(photo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -22,7 +22,7 @@ export class Photo {
|
|||||||
return Utils.findClosest(renderSize, Config.Client.thumbnailSizes);
|
return Utils.findClosest(renderSize, Config.Client.thumbnailSizes);
|
||||||
}
|
}
|
||||||
|
|
||||||
getReplacementThumbnailSize() {
|
getReplacementThumbnailSize(): number {
|
||||||
|
|
||||||
if (this.replacementSizeCache === false) {
|
if (this.replacementSizeCache === false) {
|
||||||
this.replacementSizeCache = null;
|
this.replacementSizeCache = null;
|
||||||
@ -37,7 +37,7 @@ export class Photo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return this.replacementSizeCache;
|
return <number>this.replacementSizeCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
isReplacementThumbnailAvailable() {
|
isReplacementThumbnailAvailable() {
|
||||||
@ -59,22 +59,5 @@ export class Photo {
|
|||||||
return Utils.concatUrls("/api/gallery/content/", this.photo.directory.path, this.photo.directory.name, this.photo.name, "thumbnail", size.toString());
|
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
equals(other: any) {
|
|
||||||
//is gridphoto
|
|
||||||
if (other.photo) {
|
|
||||||
return this.photo.directory.path === other.photo.directory.path && this.photo.directory.name === other.photo.directory.name && this.photo.name === other.photo.name
|
|
||||||
}
|
|
||||||
|
|
||||||
//is photo
|
|
||||||
if (other.directory) {
|
|
||||||
return this.photo.directory.path === other.directory.path && this.photo.directory.name === other.directory.name && this.photo.name === other.name
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@ -3,7 +3,7 @@ import {DirectoryDTO} from "../../../../common/entities/DirectoryDTO";
|
|||||||
import {RouterLink} from "@angular/router";
|
import {RouterLink} from "@angular/router";
|
||||||
import {Utils} from "../../../../common/Utils";
|
import {Utils} from "../../../../common/Utils";
|
||||||
import {Photo} from "../Photo";
|
import {Photo} from "../Photo";
|
||||||
import {Thumbnail, ThumbnailManagerService} from "../grid/thumnailManager.service";
|
import {Thumbnail, ThumbnailManagerService} from "../thumnailManager.service";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'gallery-directory',
|
selector: 'gallery-directory',
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import {GridPhoto} from "../GridPhoto";
|
|||||||
import {SearchTypes} from "../../../../../common/entities/AutoCompleteItem";
|
import {SearchTypes} from "../../../../../common/entities/AutoCompleteItem";
|
||||||
import {RouterLink} from "@angular/router";
|
import {RouterLink} from "@angular/router";
|
||||||
import {Config} from "../../../config/Config";
|
import {Config} from "../../../config/Config";
|
||||||
import {Thumbnail, ThumbnailManagerService} from "../thumnailManager.service";
|
import {Thumbnail, ThumbnailManagerService} from "../../thumnailManager.service";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'gallery-grid-photo',
|
selector: 'gallery-grid-photo',
|
||||||
|
|||||||
@ -2,8 +2,9 @@ import {Component, Input, OnChanges, ElementRef, ViewChild} from "@angular/core"
|
|||||||
import {PhotoDTO} from "../../../../../common/entities/PhotoDTO";
|
import {PhotoDTO} from "../../../../../common/entities/PhotoDTO";
|
||||||
import {Dimension} from "../../../model/IRenderable";
|
import {Dimension} from "../../../model/IRenderable";
|
||||||
import {FullScreenService} from "../../fullscreen.service";
|
import {FullScreenService} from "../../fullscreen.service";
|
||||||
import {Utils} from "../../../../../common/Utils";
|
|
||||||
import {SebmGoogleMap} from "angular2-google-maps/core";
|
import {SebmGoogleMap} from "angular2-google-maps/core";
|
||||||
|
import {ThumbnailManagerService, IconThumbnail} from "../../thumnailManager.service";
|
||||||
|
import {IconPhoto} from "../../IconPhoto";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'gallery-map-lightbox',
|
selector: 'gallery-map-lightbox',
|
||||||
@ -18,7 +19,7 @@ export class GalleryMapLightboxComponent implements OnChanges {
|
|||||||
public mapDimension: Dimension = <Dimension>{top: 0, left: 0, width: 0, height: 0};
|
public mapDimension: Dimension = <Dimension>{top: 0, left: 0, width: 0, height: 0};
|
||||||
private visible = false;
|
private visible = false;
|
||||||
private opacity = 1.0;
|
private opacity = 1.0;
|
||||||
mapPhotos: Array<{latitude: number, longitude: number, iconUrl}> = [];
|
mapPhotos: Array<{latitude: number, longitude: number, iconUrl?: string, thumbnail: IconThumbnail}> = [];
|
||||||
mapCenter = {latitude: 0, longitude: 0};
|
mapCenter = {latitude: 0, longitude: 0};
|
||||||
|
|
||||||
@ViewChild("root") elementRef: ElementRef;
|
@ViewChild("root") elementRef: ElementRef;
|
||||||
@ -26,7 +27,7 @@ export class GalleryMapLightboxComponent implements OnChanges {
|
|||||||
@ViewChild(SebmGoogleMap) map: SebmGoogleMap;
|
@ViewChild(SebmGoogleMap) map: SebmGoogleMap;
|
||||||
|
|
||||||
|
|
||||||
constructor(private fullScreenService: FullScreenService) {
|
constructor(private fullScreenService: FullScreenService, private thumbnailService: ThumbnailManagerService) {
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -81,21 +82,33 @@ export class GalleryMapLightboxComponent implements OnChanges {
|
|||||||
this.opacity = 0.0;
|
this.opacity = 0.0;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.visible = false;
|
this.visible = false;
|
||||||
this.mapPhotos = [];
|
this.hideImages();
|
||||||
}, 500);
|
}, 500);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
showImages() {
|
showImages() {
|
||||||
|
this.hideImages();
|
||||||
|
|
||||||
this.mapPhotos = this.photos.filter(p => {
|
this.mapPhotos = this.photos.filter(p => {
|
||||||
return p.metadata && p.metadata.positionData && p.metadata.positionData.GPSData;
|
return p.metadata && p.metadata.positionData && p.metadata.positionData.GPSData;
|
||||||
}).map(p => {
|
}).map(p => {
|
||||||
return {
|
let th = this.thumbnailService.getIcon(new IconPhoto(p));
|
||||||
|
let obj: {latitude: number, longitude: number, iconUrl?: string, thumbnail: IconThumbnail} = {
|
||||||
latitude: p.metadata.positionData.GPSData.latitude,
|
latitude: p.metadata.positionData.GPSData.latitude,
|
||||||
longitude: p.metadata.positionData.GPSData.longitude,
|
longitude: p.metadata.positionData.GPSData.longitude,
|
||||||
iconUrl: Utils.concatUrls("/api/gallery/content/", p.directory.path, p.directory.name, p.name, "icon")
|
thumbnail: th
|
||||||
|
|
||||||
};
|
};
|
||||||
|
if (th.Available == true) {
|
||||||
|
obj.iconUrl = th.Src;
|
||||||
|
} else {
|
||||||
|
th.OnLoad = () => {
|
||||||
|
obj.iconUrl = th.Src;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (this.mapPhotos.length > 0) {
|
if (this.mapPhotos.length > 0) {
|
||||||
@ -103,6 +116,11 @@ export class GalleryMapLightboxComponent implements OnChanges {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hideImages() {
|
||||||
|
this.mapPhotos.forEach(mp => mp.thumbnail.destroy());
|
||||||
|
this.mapPhotos = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private getBodyScrollTop(): number {
|
private getBodyScrollTop(): number {
|
||||||
return window.scrollY;
|
return window.scrollY;
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
import {Component, OnChanges, Input, ViewChild, ElementRef} from "@angular/core";
|
import {Component, OnChanges, Input, ViewChild, ElementRef} from "@angular/core";
|
||||||
import {PhotoDTO} from "../../../../common/entities/PhotoDTO";
|
import {PhotoDTO} from "../../../../common/entities/PhotoDTO";
|
||||||
import {Utils} from "../../../../common/Utils";
|
|
||||||
import {IRenderable, Dimension} from "../../model/IRenderable";
|
import {IRenderable, Dimension} from "../../model/IRenderable";
|
||||||
import {GalleryMapLightboxComponent} from "./lightbox/lightbox.map.gallery.component";
|
import {GalleryMapLightboxComponent} from "./lightbox/lightbox.map.gallery.component";
|
||||||
@Component({
|
@Component({
|
||||||
@ -13,7 +12,7 @@ export class GalleryMapComponent implements OnChanges, IRenderable {
|
|||||||
@Input() photos: Array<PhotoDTO>;
|
@Input() photos: Array<PhotoDTO>;
|
||||||
@ViewChild(GalleryMapLightboxComponent) mapLightbox: GalleryMapLightboxComponent;
|
@ViewChild(GalleryMapLightboxComponent) mapLightbox: GalleryMapLightboxComponent;
|
||||||
|
|
||||||
mapPhotos: Array<{latitude: number, longitude: number, iconUrl}> = [];
|
mapPhotos: Array<{latitude: number, longitude: number}> = [];
|
||||||
mapCenter = {latitude: 0, longitude: 0};
|
mapCenter = {latitude: 0, longitude: 0};
|
||||||
@ViewChild("map") map: ElementRef;
|
@ViewChild("map") map: ElementRef;
|
||||||
|
|
||||||
@ -24,8 +23,7 @@ export class GalleryMapComponent implements OnChanges, IRenderable {
|
|||||||
}).map(p => {
|
}).map(p => {
|
||||||
return {
|
return {
|
||||||
latitude: p.metadata.positionData.GPSData.latitude,
|
latitude: p.metadata.positionData.GPSData.latitude,
|
||||||
longitude: p.metadata.positionData.GPSData.longitude,
|
longitude: p.metadata.positionData.GPSData.longitude
|
||||||
iconUrl: Utils.concatUrls("/api/gallery/content/", p.directory.path, p.directory.name, p.name, "icon")
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,9 @@
|
|||||||
import {Injectable} from "@angular/core";
|
import {Injectable} from "@angular/core";
|
||||||
import {Config} from "../../config/Config";
|
import {Config} from "../config/Config";
|
||||||
import {GalleryCacheService} from "../cache.gallery.service";
|
import {GalleryCacheService} from "./cache.gallery.service";
|
||||||
import {Photo} from "../Photo";
|
import {Photo} from "./Photo";
|
||||||
|
import {IconPhoto} from "./IconPhoto";
|
||||||
|
import {PhotoDTO} from "../../../common/entities/PhotoDTO";
|
||||||
|
|
||||||
export enum ThumbnailLoadingPriority{
|
export enum ThumbnailLoadingPriority{
|
||||||
high, medium, low
|
high, medium, low
|
||||||
@ -36,12 +38,46 @@ export class ThumbnailLoaderService {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
loadIcon(photo: IconPhoto, priority: ThumbnailLoadingPriority, listener: ThumbnailLoadingListener): ThumbnailTaskEntity {
|
||||||
|
let tmp: ThumbnailTask = null;
|
||||||
|
//is image already qued?
|
||||||
|
for (let i = 0; i < this.que.length; i++) {
|
||||||
|
if (this.que[i].path == photo.getIconPath()) {
|
||||||
|
tmp = this.que[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let thumbnailTaskEntity = {priority: priority, listener: listener};
|
||||||
|
//add to previous
|
||||||
|
if (tmp != null) {
|
||||||
|
tmp.taskEntities.push(thumbnailTaskEntity);
|
||||||
|
if (tmp.inProgress == true) {
|
||||||
|
listener.onStartedLoading();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} else {//create new task
|
||||||
|
this.que.push(<ThumbnailTask>{
|
||||||
|
photo: photo.photo,
|
||||||
|
inProgress: false,
|
||||||
|
taskEntities: [thumbnailTaskEntity],
|
||||||
|
onLoaded: () => {
|
||||||
|
photo.iconLoaded();
|
||||||
|
},
|
||||||
|
path: photo.getIconPath()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
setImmediate(this.run);
|
||||||
|
return thumbnailTaskEntity;
|
||||||
|
}
|
||||||
|
|
||||||
loadImage(photo: Photo, priority: ThumbnailLoadingPriority, listener: ThumbnailLoadingListener): ThumbnailTaskEntity {
|
loadImage(photo: Photo, priority: ThumbnailLoadingPriority, listener: ThumbnailLoadingListener): ThumbnailTaskEntity {
|
||||||
|
|
||||||
let tmp: ThumbnailTask = null;
|
let tmp: ThumbnailTask = null;
|
||||||
//is image already qued?
|
//is image already qued?
|
||||||
for (let i = 0; i < this.que.length; i++) {
|
for (let i = 0; i < this.que.length; i++) {
|
||||||
if (this.que[i].photo.getThumbnailPath() == photo.getThumbnailPath()) {
|
if (this.que[i].path == photo.getThumbnailPath()) {
|
||||||
tmp = this.que[i];
|
tmp = this.que[i];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -58,9 +94,13 @@ export class ThumbnailLoaderService {
|
|||||||
|
|
||||||
} else {//create new task
|
} else {//create new task
|
||||||
this.que.push({
|
this.que.push({
|
||||||
photo: photo,
|
photo: photo.photo,
|
||||||
inProgress: false,
|
inProgress: false,
|
||||||
taskEntities: [thumbnailTaskEntity]
|
taskEntities: [thumbnailTaskEntity],
|
||||||
|
onLoaded: () => {
|
||||||
|
photo.thumbnailLoaded();
|
||||||
|
},
|
||||||
|
path: photo.getThumbnailPath()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
setImmediate(this.run);
|
setImmediate(this.run);
|
||||||
@ -129,8 +169,8 @@ export class ThumbnailLoaderService {
|
|||||||
|
|
||||||
let curImg = new Image();
|
let curImg = new Image();
|
||||||
curImg.onload = () => {
|
curImg.onload = () => {
|
||||||
task.photo.thumbnailLoaded();
|
task.onLoaded();
|
||||||
this.galleryChacheService.photoUpdated(task.photo.photo);
|
this.galleryChacheService.photoUpdated(task.photo);
|
||||||
task.taskEntities.forEach((te: ThumbnailTaskEntity) => te.listener.onLoad());
|
task.taskEntities.forEach((te: ThumbnailTaskEntity) => te.listener.onLoad());
|
||||||
|
|
||||||
this.taskReady(task);
|
this.taskReady(task);
|
||||||
@ -146,7 +186,7 @@ export class ThumbnailLoaderService {
|
|||||||
this.run();
|
this.run();
|
||||||
};
|
};
|
||||||
|
|
||||||
curImg.src = task.photo.getThumbnailPath();
|
curImg.src = task.path;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,8 +204,10 @@ export interface ThumbnailTaskEntity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface ThumbnailTask {
|
interface ThumbnailTask {
|
||||||
photo: Photo;
|
photo: PhotoDTO;
|
||||||
inProgress: boolean;
|
inProgress: boolean;
|
||||||
taskEntities: Array<ThumbnailTaskEntity>;
|
taskEntities: Array<ThumbnailTaskEntity>;
|
||||||
|
path: string;
|
||||||
|
onLoaded: Function;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1,6 +1,7 @@
|
|||||||
import {Injectable} from "@angular/core";
|
import {Injectable} from "@angular/core";
|
||||||
import {Photo} from "../Photo";
|
|
||||||
import {ThumbnailLoaderService, ThumbnailLoadingListener, ThumbnailTaskEntity} from "./thumnailLoader.service";
|
import {ThumbnailLoaderService, ThumbnailLoadingListener, ThumbnailTaskEntity} from "./thumnailLoader.service";
|
||||||
|
import {Photo} from "./Photo";
|
||||||
|
import {IconPhoto} from "./IconPhoto";
|
||||||
|
|
||||||
export enum ThumbnailLoadingPriority{
|
export enum ThumbnailLoadingPriority{
|
||||||
high, medium, low
|
high, medium, low
|
||||||
@ -16,21 +17,117 @@ export class ThumbnailManagerService {
|
|||||||
public getThumbnail(photo: Photo) {
|
public getThumbnail(photo: Photo) {
|
||||||
return new Thumbnail(photo, this.thumbnailLoader);
|
return new Thumbnail(photo, this.thumbnailLoader);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public getIcon(photo: IconPhoto) {
|
||||||
|
return new IconThumbnail(photo, this.thumbnailLoader);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export class Thumbnail {
|
export abstract class ThumbnailBase {
|
||||||
|
|
||||||
private available: boolean = false;
|
protected available: boolean = false;
|
||||||
private src: string = null;
|
protected src: string = null;
|
||||||
private loading: boolean = false;
|
protected loading: boolean = false;
|
||||||
private thumbnailTask: ThumbnailTaskEntity;
|
protected onLoad: Function = null;
|
||||||
|
protected thumbnailTask: ThumbnailTaskEntity;
|
||||||
|
|
||||||
|
|
||||||
constructor(private photo: Photo, private thumbnailService: ThumbnailLoaderService) {
|
constructor(protected thumbnailService: ThumbnailLoaderService) {
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract set Visible(visible: boolean);
|
||||||
|
|
||||||
|
set OnLoad(onLoad: Function) {
|
||||||
|
this.onLoad = onLoad;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
get Available() {
|
||||||
|
return this.available;
|
||||||
|
}
|
||||||
|
|
||||||
|
get Src() {
|
||||||
|
return this.src;
|
||||||
|
}
|
||||||
|
|
||||||
|
get Loading() {
|
||||||
|
return this.loading;
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
if (this.thumbnailTask != null) {
|
||||||
|
this.thumbnailService.removeTask(this.thumbnailTask);
|
||||||
|
this.thumbnailTask = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export class IconThumbnail extends ThumbnailBase {
|
||||||
|
|
||||||
|
constructor(private photo: IconPhoto, thumbnailService: ThumbnailLoaderService) {
|
||||||
|
super(thumbnailService);
|
||||||
|
this.src = "";
|
||||||
|
if (this.photo.isIconAvailable()) {
|
||||||
|
this.src = this.photo.getIconPath();
|
||||||
|
this.available = true;
|
||||||
|
if (this.onLoad) this.onLoad();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.photo.isIconAvailable()) {
|
||||||
|
setImmediate(() => {
|
||||||
|
|
||||||
|
let listener: ThumbnailLoadingListener = {
|
||||||
|
onStartedLoading: () => { //onLoadStarted
|
||||||
|
this.loading = true;
|
||||||
|
},
|
||||||
|
onLoad: () => {//onLoaded
|
||||||
|
this.src = this.photo.getIconPath();
|
||||||
|
if (this.onLoad) this.onLoad();
|
||||||
|
this.available = true;
|
||||||
|
this.loading = false;
|
||||||
|
this.thumbnailTask = null;
|
||||||
|
},
|
||||||
|
onError: (error) => {//onError
|
||||||
|
this.thumbnailTask = null;
|
||||||
|
//TODO: handle error
|
||||||
|
//TODO: not an error if its from cache
|
||||||
|
console.error("something bad happened");
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.thumbnailTask = this.thumbnailService.loadIcon(this.photo, ThumbnailLoadingPriority.high, listener);
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
set Visible(visible: boolean) {
|
||||||
|
if (!this.thumbnailTask) return;
|
||||||
|
if (visible === true) {
|
||||||
|
this.thumbnailTask.priority = ThumbnailLoadingPriority.high;
|
||||||
|
} else {
|
||||||
|
this.thumbnailTask.priority = ThumbnailLoadingPriority.medium;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Thumbnail extends ThumbnailBase {
|
||||||
|
|
||||||
|
|
||||||
|
constructor(private photo: Photo, thumbnailService: ThumbnailLoaderService) {
|
||||||
|
super(thumbnailService);
|
||||||
if (this.photo.isThumbnailAvailable()) {
|
if (this.photo.isThumbnailAvailable()) {
|
||||||
this.src = this.photo.getThumbnailPath();
|
this.src = this.photo.getThumbnailPath();
|
||||||
this.available = true;
|
this.available = true;
|
||||||
|
if (this.onLoad) this.onLoad();
|
||||||
} else if (this.photo.isReplacementThumbnailAvailable()) {
|
} else if (this.photo.isReplacementThumbnailAvailable()) {
|
||||||
this.src = this.photo.getReplacementThumbnailPath();
|
this.src = this.photo.getReplacementThumbnailPath();
|
||||||
this.available = true;
|
this.available = true;
|
||||||
@ -45,6 +142,7 @@ export class Thumbnail {
|
|||||||
},
|
},
|
||||||
onLoad: () => {//onLoaded
|
onLoad: () => {//onLoaded
|
||||||
this.src = this.photo.getThumbnailPath();
|
this.src = this.photo.getThumbnailPath();
|
||||||
|
if (this.onLoad) this.onLoad();
|
||||||
this.available = true;
|
this.available = true;
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
this.thumbnailTask = null;
|
this.thumbnailTask = null;
|
||||||
@ -70,6 +168,7 @@ export class Thumbnail {
|
|||||||
}
|
}
|
||||||
|
|
||||||
set Visible(visible: boolean) {
|
set Visible(visible: boolean) {
|
||||||
|
if (!this.thumbnailTask) return;
|
||||||
if (visible === true) {
|
if (visible === true) {
|
||||||
if (this.photo.isReplacementThumbnailAvailable()) {
|
if (this.photo.isReplacementThumbnailAvailable()) {
|
||||||
this.thumbnailTask.priority = ThumbnailLoadingPriority.medium;
|
this.thumbnailTask.priority = ThumbnailLoadingPriority.medium;
|
||||||
@ -86,24 +185,5 @@ export class Thumbnail {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
get Available() {
|
|
||||||
return this.available;
|
|
||||||
}
|
|
||||||
|
|
||||||
get Src() {
|
|
||||||
return this.src;
|
|
||||||
}
|
|
||||||
|
|
||||||
get Loading() {
|
|
||||||
return this.loading;
|
|
||||||
}
|
|
||||||
|
|
||||||
destroy() {
|
|
||||||
if (this.thumbnailTask != null) {
|
|
||||||
this.thumbnailService.removeTask(this.thumbnailTask);
|
|
||||||
this.thumbnailTask = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Loading…
Reference in New Issue
Block a user