1
0
mirror of https://github.com/bpatrik/pigallery2.git synced 2025-02-13 13:59:36 +02:00

improving API performance by removing null properties

adding photo caption reading
This commit is contained in:
Patrik J. Braun 2018-12-05 17:29:33 +01:00
parent 77a815fe53
commit 609c788d91
12 changed files with 100 additions and 57 deletions

View File

@ -4,18 +4,8 @@ language: node_js
node_js:
- '8'
- '10'
- '11'
deploy:
provider: heroku
api_key:
secure: kv4RwF5ZGYUbgmaX9jESGAasxKPMSkAD8NUShwIE+H//Z/xPtnyJ4zbIYb+NTPljS2lZGXGxxhtxVzC7bX+q/Y+k7aKjY1gcZHEXWKHF2RIIB/KTESonuootvvetbKQVYx5bqAakCXkXmbq+/3yRD89q6PJMGsw5rCx7fEpOil3yITfneRGul/8ZDhtgGLSQtsa0iqVHVYnYFnEI1B2EsYHrCmyGWFen0wKKZkqE2ryxw2KevsOEm7dlz4xtjIQP/zTdFDwCL1IqkXYpvGMMBnnntGkPQBjGoRiJfYRQVQi+XC3qhPg+XG/SdoExiHoEc+uOlf8VqwTn3Z1uPEvMzZP+02r5EhupeOZ9rMXTgb6EYXN6q8i5agkXF8QujUYFz5NZs451YF3PFxyq7KKTrtuKd0KujGOkVzA0KpIjl2tRztxvej3Q2IPblAMXH+Rq9pem/HAH2Oxr+stT7dIOawHh5bk3yTMuLDvsEFbneELEHStzWzWFIhyBIXtTEEdSJY9moh76lkZY83ireE6U3zmLftJ7+TWBdFTiUe0mJxPoI8MWAr1rjcXNVjE7iUXx8q4rNPhVlJ3uzKk+qZ+P5VjNQLUAT1QE/IdF6h7V7nVcn5XeVPvIIcIa5b1tBTqmYBO42S4CkQ+plXsfVbiKACgPEmkeGU9bIqomQaFlcbQ=
app: pigallery2
on:
repo: bpatrik/pigallery2
node: '8'
cache:
directories:
- node_modules
addons:
chrome: stable

View File

@ -13,6 +13,7 @@ import {UserDTO} from '../../common/entities/UserDTO';
import {RandomQuery} from '../model/interfaces/IGalleryManager';
import {MediaDTO} from '../../common/entities/MediaDTO';
import {VideoDTO} from '../../common/entities/VideoDTO';
import {Utils} from '../../common/Utils';
const LOG_TAG = '[GalleryMWs]';
@ -68,12 +69,16 @@ export class GalleryMWs {
delete (<VideoDTO>m).metadata.bitRate;
delete (<VideoDTO>m).metadata.duration;
} else if (MediaDTO.isVideo(m)) {
delete (<PhotoDTO>m).metadata.caption;
delete (<PhotoDTO>m).metadata.cameraData;
delete (<PhotoDTO>m).metadata.orientation;
delete (<PhotoDTO>m).metadata.orientation;
delete (<PhotoDTO>m).metadata.keywords;
delete (<PhotoDTO>m).metadata.positionData;
}
Utils.removeNullOrEmptyObj(m);
console.log(m);
console.log(Utils.removeNullOrEmptyObj(m));
});
};

View File

@ -17,6 +17,8 @@ export class MediaDimensionEntity implements MediaDimension {
export class MediaMetadataEntity implements MediaMetadata {
@Column('text')
caption: string;
@Column(type => MediaDimensionEntity)
size: MediaDimensionEntity;
@ -50,7 +52,7 @@ export class MediaMetadataEntity implements MediaMetadata {
// TODO: fix inheritance once its working in typeorm
@Entity()
@TableInheritance({column: {type: 'varchar', name: 'type'}})
export abstract class MediaEntity implements MediaDTO {
export abstract class MediaEntity implements MediaDTO {
@PrimaryGeneratedColumn()
id: number;

View File

@ -18,8 +18,9 @@ const LOG_TAG = '[DiskManagerTask]';
const ffmpeg = FFmpegFactory.get();
export class DiskMangerWorker {
private static isImage(fullPath: string) {
const extensions = [
private static readonly SupportedEXT = {
photo: [
'.bmp',
'.gif',
'.jpeg', '.jpg', '.jpe',
@ -28,31 +29,31 @@ export class DiskMangerWorker {
'.webp',
'.ico',
'.tga'
];
const extension = path.extname(fullPath).toLowerCase();
return extensions.indexOf(extension) !== -1;
}
private static isVideo(fullPath: string) {
const extensions = [
],
video: [
'.mp4',
'.webm',
'.ogv',
'.ogg'
];
],
metaFile: [
'.gpx'
]
};
private static isImage(fullPath: string) {
const extension = path.extname(fullPath).toLowerCase();
return extensions.indexOf(extension) !== -1;
return this.SupportedEXT.photo.indexOf(extension) !== -1;
}
private static isVideo(fullPath: string) {
const extension = path.extname(fullPath).toLowerCase();
return this.SupportedEXT.video.indexOf(extension) !== -1;
}
private static isMetaFile(fullPath: string) {
const extensions = [
'.gpx'
];
const extension = path.extname(fullPath).toLowerCase();
return extensions.indexOf(extension) !== -1;
return this.SupportedEXT.metaFile.indexOf(extension) !== -1;
}
public static scanDirectory(relativeDirectoryName: string, maxPhotos: number = null, photosOnly: boolean = false): Promise<DirectoryDTO> {
@ -133,7 +134,7 @@ export class DiskMangerWorker {
public static loadVideoMetadata(fullPath: string): Promise<VideoMetadata> {
return new Promise<VideoMetadata>((resolve, reject) => {
const metadata: VideoMetadata = <VideoMetadata>{
const metadata: VideoMetadata = {
size: {
width: 1,
height: 1
@ -188,11 +189,12 @@ export class DiskMangerWorker {
fs.closeSync(fd);
return reject({file: fullPath, error: err});
}
const metadata: PhotoMetadata = <PhotoMetadata>{
const metadata: PhotoMetadata = {
keywords: [],
cameraData: {},
positionData: null,
size: {},
size: {width: 1, height: 1},
caption: null,
orientation: OrientationTypes.TOP_LEFT,
creationDate: 0,
fileSize: 0
@ -253,7 +255,8 @@ export class DiskMangerWorker {
metadata.positionData.state = iptcData.province_or_state;
metadata.positionData.city = iptcData.city;
}
metadata.keywords = <string[]>(iptcData.keywords || []);
metadata.caption = iptcData.caption;
metadata.keywords = iptcData.keywords || [];
metadata.creationDate = <number>(iptcData.date_time ? iptcData.date_time.getTime() : metadata.creationDate);
} catch (err) {

View File

@ -1 +1 @@
export const DataStructureVersion = 3;
export const DataStructureVersion = 4;

View File

@ -8,6 +8,27 @@ export class Utils {
}
static removeNullOrEmptyObj(obj: any) {
if (typeof obj !== 'object' || obj == null) {
return obj;
}
const keys = Object.keys(obj);
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
if (obj[key] !== null && typeof obj[key] === 'object') {
if (Utils.removeNullOrEmptyObj(obj[key])) {
if (Object.keys(obj[key]).length === 0) {
delete obj[key];
}
}
} else if (obj[key] === null) {
delete obj[key];
}
}
return obj;
}
static clone<T>(object: T): T {
return JSON.parse(JSON.stringify(object));
}

View File

@ -4,7 +4,7 @@ import {OrientationTypes} from 'ts-exif-parser';
import {VideoDTO} from './VideoDTO';
import {FileDTO} from './FileDTO';
export interface MediaDTO extends FileDTO{
export interface MediaDTO extends FileDTO {
id: number;
name: string;
directory: DirectoryDTO;
@ -51,11 +51,12 @@ export module MediaDTO {
};
export const isPhoto = (media: MediaDTO): boolean => {
return typeof (<PhotoDTO>media).metadata.keywords !== 'undefined' && (<PhotoDTO>media).metadata.keywords !== null;
return !MediaDTO.isVideo(media);
};
export const isVideo = (media: MediaDTO): boolean => {
return !MediaDTO.isPhoto(media);
const lower = media.name.toLowerCase();
return lower.endsWith('.mp4') || lower.endsWith('.webm') || lower.endsWith('.ogg') || lower.endsWith('.ogv');
};
export const getRotatedSize = (photo: MediaDTO): MediaDimension => {

View File

@ -12,6 +12,7 @@ export interface PhotoDTO extends MediaDTO {
}
export interface PhotoMetadata extends MediaMetadata {
caption: string;
keywords: Array<string>;
cameraData: CameraMetadata;
positionData: PositionMetaData;

View File

@ -1,5 +1,4 @@
import {Injectable} from '@angular/core';
import {Location} from '@angular/common';
import {NetworkService} from '../model/network/network.service';
import {ContentWrapper} from '../../../common/entities/ConentWrapper';
import {DirectoryDTO} from '../../../common/entities/DirectoryDTO';
@ -11,6 +10,7 @@ import {Config} from '../../../common/config/public/Config';
import {ShareService} from './share.service';
import {NavigationService} from '../model/navigation.service';
import {SortingMethods} from '../../../common/entities/SortingMethods';
import {QueryService} from '../model/query.service';
import {QueryParams} from '../../../common/QueryParams';
@ -34,8 +34,7 @@ export class GalleryService {
constructor(private networkService: NetworkService,
private galleryCacheService: GalleryCacheService,
private _shareService: ShareService,
private navigationService: NavigationService,
private location: Location) {
private navigationService: NavigationService) {
this.content = new BehaviorSubject<ContentWrapper>(new ContentWrapper());
this.sorting = new BehaviorSubject<SortingMethods>(Config.Client.Other.defaultPhotoSortingMethod);
}
@ -48,7 +47,7 @@ export class GalleryService {
this.sorting.next(sorting);
}
public loadDirectory(directoryName: string): void {
public async loadDirectory(directoryName: string): Promise<void> {
const content = new ContentWrapper();
content.directory = this.galleryCacheService.getDirectory(directoryName);
@ -71,8 +70,8 @@ export class GalleryService {
params['knownLastScanned'] = content.directory.lastScanned;
}
this.networkService.getJson<ContentWrapper>('/gallery/content/' + directoryName, params).then((cw) => {
try {
const cw = await this.networkService.getJson<ContentWrapper>('/gallery/content/' + directoryName, params);
if (!cw || cw.notModified === true) {
@ -85,18 +84,14 @@ export class GalleryService {
return;
}
DirectoryDTO.addReferences(<DirectoryDTO>cw.directory);
this.lastDirectory = <DirectoryDTO>cw.directory;
this.content.next(cw);
}).catch((e) => {
} catch (e) {
console.error(e);
this.navigationService.toGallery().catch(console.error);
});
}
}
public async search(text: string, type?: SearchTypes): Promise<void> {
@ -140,7 +135,7 @@ export class GalleryService {
clearTimeout(this.searchId);
}
if (!this.lastDirectory) {
this.loadDirectory('/');
this.loadDirectory('/').catch(console.error);
}
return null;
}
@ -158,7 +153,7 @@ export class GalleryService {
if (cw.searchResult == null) {
// If result is not search cache, try to load more
this.searchId = setTimeout(() => {
this.search(text, type);
this.search(text, type).catch(console.error);
this.searchId = null;
}, Config.Client.Search.InstantSearchTimeout);

View File

@ -5,6 +5,8 @@ import {MediaDTO} from '../../../common/entities/MediaDTO';
import {QueryParams} from '../../../common/QueryParams';
import {Utils} from '../../../common/Utils';
import {GalleryService} from '../gallery/gallery.service';
import {Config} from '../../../common/config/public/Config';
import {DirectoryDTO} from '../../../common/entities/DirectoryDTO';
@Injectable()
export class QueryService {
@ -27,10 +29,29 @@ export class QueryService {
if (media) {
query[QueryParams.gallery.photo] = this.getMediaStringId(media);
}
if (this.shareService.isSharing()) {
query[QueryParams.gallery.sharingKey_short] = this.shareService.getSharingKey();
if (Config.Client.Sharing.enabled === true) {
if (this.shareService.isSharing()) {
query[QueryParams.gallery.sharingKey_short] = this.shareService.getSharingKey();
}
}
return query;
}
getParamsForDirs(directory: DirectoryDTO) {
const params: { [key: string]: any } = {};
if (Config.Client.Sharing.enabled === true) {
if (this.shareService.isSharing()) {
params[QueryParams.gallery.sharingKey_short] = this.shareService.getSharingKey();
}
}
if (directory && directory.lastModified && directory.lastScanned &&
!directory.isPartial) {
params['knownLastModified'] = directory.lastModified;
params['knownLastScanned'] = directory.lastScanned;
}
return params;
}
}

View File

@ -113,8 +113,8 @@ describe('GalleryManager', () => {
subDir.isPartial = true;
delete subDir.directories;
delete subDir.metaFile;
expect(Utils.clone(selected)).to.deep.equal(Utils.clone(parent));
expect(Utils.clone(Utils.removeNullOrEmptyObj(selected)))
.to.deep.equal(Utils.clone(Utils.removeNullOrEmptyObj(parent)));
});
it('should skip meta files', async () => {
@ -135,7 +135,8 @@ describe('GalleryManager', () => {
delete parent.metaFile;
DirectoryDTO.removeReferences(selected);
removeIds(selected);
expect(Utils.clone(selected)).to.deep.equal(Utils.clone(parent));
expect(Utils.clone(Utils.removeNullOrEmptyObj(selected)))
.to.deep.equal(Utils.clone(Utils.removeNullOrEmptyObj(parent)));
});
it('should update sub directory', async () => {
@ -168,7 +169,8 @@ describe('GalleryManager', () => {
delete subDir.metaFile;
removeIds(selected);
// selected.directories[0].parent = selected;
expect(Utils.clone(selected)).to.deep.equal(Utils.clone(subDir));
expect(Utils.clone(Utils.removeNullOrEmptyObj(selected)))
.to.deep.equal(Utils.clone(Utils.removeNullOrEmptyObj(subDir)));
});

View File

@ -46,6 +46,7 @@ export class TestHelper {
cd.focalLength = 1;
cd.lens = 'Lens';
const m = new PhotoMetadataEntity();
m.caption = null;
m.keywords = ['apple'];
m.cameraData = cd;
m.positionData = pd;
@ -72,6 +73,7 @@ export class TestHelper {
sd.width = 200;
const m = new VideoMetadataEntity();
m.caption = null;
m.keywords = null;
m.size = sd;
m.creationDate = Date.now();