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:
parent
77a815fe53
commit
609c788d91
12
.travis.yml
12
.travis.yml
@ -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
|
||||
|
@ -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));
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -1 +1 @@
|
||||
export const DataStructureVersion = 3;
|
||||
export const DataStructureVersion = 4;
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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 => {
|
||||
|
@ -12,6 +12,7 @@ export interface PhotoDTO extends MediaDTO {
|
||||
}
|
||||
|
||||
export interface PhotoMetadata extends MediaMetadata {
|
||||
caption: string;
|
||||
keywords: Array<string>;
|
||||
cameraData: CameraMetadata;
|
||||
positionData: PositionMetaData;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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)));
|
||||
|
||||
});
|
||||
|
||||
|
@ -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();
|
||||
|
Loading…
x
Reference in New Issue
Block a user