From 3541eae141e4628b20f80fa2e7a854d0946e98b4 Mon Sep 17 00:00:00 2001 From: "Patrik J. Braun" Date: Sat, 19 Jun 2021 11:20:40 +0200 Subject: [PATCH] Implementing listing metafiles in search result --- package-lock.json | 6 +- .../model/database/sql/SearchManager.ts | 21 ++++- .../database/sql/enitites/MediaEntity.ts | 78 ++++++++++++++++++- .../database/sql/enitites/PhotoEntity.ts | 71 ----------------- src/common/config/public/ClientConfig.ts | 4 + .../search/search.settings.component.html | 8 ++ src/frontend/translate/messages.en.xlf | 8 ++ src/frontend/translate/messages.es.xlf | 8 ++ src/frontend/translate/messages.fr.xlf | 8 ++ src/frontend/translate/messages.hu.xlf | 8 ++ src/frontend/translate/messages.it.xlf | 8 ++ src/frontend/translate/messages.pl.xlf | 8 ++ src/frontend/translate/messages.ro.xlf | 8 ++ src/frontend/translate/messages.ru.xlf | 8 ++ src/frontend/translate/messages.sv.xlf | 8 ++ test/backend/integration/model/sql/typeorm.ts | 9 +-- .../unit/model/sql/SearchManager.spec.ts | 3 + test/backend/unit/model/sql/TestHelper.ts | 7 +- 18 files changed, 193 insertions(+), 86 deletions(-) diff --git a/package-lock.json b/package-lock.json index 17cf570a..e1cfb183 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14296,7 +14296,8 @@ }, "lodash": { "version": "4.17.20", - "resolved": "", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", "dev": true }, "minimist": { @@ -21616,7 +21617,8 @@ }, "y18n": { "version": "3.2.1", - "resolved": "", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", "dev": true }, "yallist": { diff --git a/src/backend/model/database/sql/SearchManager.ts b/src/backend/model/database/sql/SearchManager.ts index 8af49aa5..a4c96f3a 100644 --- a/src/backend/model/database/sql/SearchManager.ts +++ b/src/backend/model/database/sql/SearchManager.ts @@ -33,6 +33,7 @@ import {ISQLGalleryManager} from './IGalleryManager'; import {ISQLSearchManager} from './ISearchManager'; import {MediaDTO} from '../../../../common/entities/MediaDTO'; import {Utils} from '../../../../common/Utils'; +import {FileEntity} from './enitites/FileEntity'; export class SearchManager implements ISQLSearchManager { @@ -156,13 +157,15 @@ export class SearchManager implements ISQLSearchManager { const facesQuery = Config.Server.Database.type === DatabaseType.mysql ? 'CONCAT(\'[\' , GROUP_CONCAT( \'{"name": "\' , person.name , \'", "box": {"top":\' , faces.box.top , \', "left":\' , faces.box.left , \', "height":\' , faces.box.height ,\', "width":\' , faces.box.width , \'}}\' ) ,\']\') as media_metadataFaces' : '\'[\' || GROUP_CONCAT( \'{"name": "\' || person.name || \'", "box": {"top":\' || faces.box.top || \', "left":\' || faces.box.left || \', "height":\' || faces.box.height ||\', "width":\' || faces.box.width || \'}}\' ) ||\']\' as media_metadataFaces'; + const directorySelect = ['directory.id', 'directory.name', 'directory.path']; + const rawAndEntries = await connection .getRepository(MediaEntity) .createQueryBuilder('media') - .select(['media', facesQuery]) + .select(['media', ...directorySelect, facesQuery]) .where(this.buildWhereQuery(query)) - .leftJoinAndSelect('media.directory', 'directory') + .leftJoin('media.directory', 'directory') .leftJoin('media.metadata.faces', 'faces') .leftJoin('faces.person', 'person') .limit(Config.Client.Search.maxMediaResult + 1) @@ -181,6 +184,20 @@ export class SearchManager implements ISQLSearchManager { result.resultOverflow = true; } + if (Config.Client.Search.listMetafiles === true) { + result.metaFile = await connection.getRepository(FileEntity) + .createQueryBuilder('file') + .select(['file', ...directorySelect]) + .innerJoin(q => q.from(MediaEntity, 'media') + .select('distinct directory.id') + .where(this.buildWhereQuery(query)) + .leftJoin('media.directory', 'directory'), + 'dir', + 'file.directory=dir.id') + .leftJoin('file.directory', 'directory') + .getMany(); + } + if (Config.Client.Search.listDirectories === true) { const dirQuery = this.filterDirectoryQuery(query); if (dirQuery !== null) { diff --git a/src/backend/model/database/sql/enitites/MediaEntity.ts b/src/backend/model/database/sql/enitites/MediaEntity.ts index 51295297..1c125a9c 100644 --- a/src/backend/model/database/sql/enitites/MediaEntity.ts +++ b/src/backend/model/database/sql/enitites/MediaEntity.ts @@ -1,10 +1,10 @@ -import {Column, Entity, Index, ManyToOne, OneToMany, PrimaryGeneratedColumn, TableInheritance, Unique, ValueTransformer} from 'typeorm'; +import {Column, Entity, Index, ManyToOne, OneToMany, PrimaryGeneratedColumn, TableInheritance, Unique} from 'typeorm'; import {DirectoryEntity} from './DirectoryEntity'; import {MediaDimension, MediaDTO, MediaMetadata} from '../../../../../common/entities/MediaDTO'; import {OrientationTypes} from 'ts-exif-parser'; -import {CameraMetadataEntity, PositionMetaDataEntity} from './PhotoEntity'; import {FaceRegionEntry} from './FaceRegionEntry'; import {columnCharsetCS} from './EntityUtils'; +import {CameraMetadata, GPSMetadata, PositionMetaData} from '../../../../../common/entities/PhotoDTO'; export class MediaDimensionEntity implements MediaDimension { @@ -16,6 +16,80 @@ export class MediaDimensionEntity implements MediaDimension { } +export class CameraMetadataEntity implements CameraMetadata { + + @Column('int', {nullable: true, unsigned: true}) + ISO: number; + + + @Column({ + type: 'text', nullable: true, + charset: columnCharsetCS.charset, + collation: columnCharsetCS.collation + }) + model: string; + + + @Column({ + type: 'text', nullable: true, + charset: columnCharsetCS.charset, + collation: columnCharsetCS.collation + }) + make: string; + + @Column('float', {nullable: true}) + fStop: number; + + @Column('float', {nullable: true}) + exposure: number; + + @Column('float', {nullable: true}) + focalLength: number; + + @Column('text', {nullable: true}) + lens: string; +} + + +export class GPSMetadataEntity implements GPSMetadata { + + @Column('float', {nullable: true}) + latitude: number; + @Column('float', {nullable: true}) + longitude: number; + @Column('int', {nullable: true}) + altitude: number; +} + + +export class PositionMetaDataEntity implements PositionMetaData { + + @Column(type => GPSMetadataEntity) + GPSData: GPSMetadataEntity; + + @Column({ + type: 'text', nullable: true, + charset: columnCharsetCS.charset, + collation: columnCharsetCS.collation + }) + country: string; + + @Column({ + type: 'text', nullable: true, + charset: columnCharsetCS.charset, + collation: columnCharsetCS.collation + }) + state: string; + + @Column({ + type: 'text', nullable: true, + charset: columnCharsetCS.charset, + collation: columnCharsetCS.collation + }) + city: string; +} + + export class MediaMetadataEntity implements MediaMetadata { @Column('text') caption: string; diff --git a/src/backend/model/database/sql/enitites/PhotoEntity.ts b/src/backend/model/database/sql/enitites/PhotoEntity.ts index 794e3490..dae4a539 100644 --- a/src/backend/model/database/sql/enitites/PhotoEntity.ts +++ b/src/backend/model/database/sql/enitites/PhotoEntity.ts @@ -3,78 +3,7 @@ import {CameraMetadata, GPSMetadata, PhotoDTO, PhotoMetadata, PositionMetaData} import {MediaEntity, MediaMetadataEntity} from './MediaEntity'; import {columnCharsetCS} from './EntityUtils'; -export class CameraMetadataEntity implements CameraMetadata { - @Column('int', {nullable: true, unsigned: true}) - ISO: number; - - - @Column({ - type: 'text', nullable: true, - charset: columnCharsetCS.charset, - collation: columnCharsetCS.collation - }) - model: string; - - - @Column({ - type: 'text', nullable: true, - charset: columnCharsetCS.charset, - collation: columnCharsetCS.collation - }) - make: string; - - @Column('float', {nullable: true}) - fStop: number; - - @Column('float', {nullable: true}) - exposure: number; - - @Column('float', {nullable: true}) - focalLength: number; - - @Column('text', {nullable: true}) - lens: string; -} - - -export class GPSMetadataEntity implements GPSMetadata { - - @Column('float', {nullable: true}) - latitude: number; - @Column('float', {nullable: true}) - longitude: number; - @Column('int', {nullable: true}) - altitude: number; -} - - -export class PositionMetaDataEntity implements PositionMetaData { - - @Column(type => GPSMetadataEntity) - GPSData: GPSMetadataEntity; - - @Column({ - type: 'text', nullable: true, - charset: columnCharsetCS.charset, - collation: columnCharsetCS.collation - }) - country: string; - - @Column({ - type: 'text', nullable: true, - charset: columnCharsetCS.charset, - collation: columnCharsetCS.collation - }) - state: string; - - @Column({ - type: 'text', nullable: true, - charset: columnCharsetCS.charset, - collation: columnCharsetCS.collation - }) - city: string; -} export class PhotoMetadataEntity extends MediaMetadataEntity implements PhotoMetadata { diff --git a/src/common/config/public/ClientConfig.ts b/src/common/config/public/ClientConfig.ts index 57bf3f0d..3db97eaf 100644 --- a/src/common/config/public/ClientConfig.ts +++ b/src/common/config/public/ClientConfig.ts @@ -31,6 +31,10 @@ export class ClientSearchConfig { maxMediaResult: number = 10000; @ConfigProperty({description: 'Search returns also with directories, not just media'}) listDirectories: boolean = false; + @ConfigProperty({ + description: 'Search also returns with metafiles from directories that contain a media file of the matched search result' + }) + listMetafiles: boolean = false; @ConfigProperty({type: 'unsignedInt'}) maxDirectoryResult: number = 200; } diff --git a/src/frontend/app/ui/settings/search/search.settings.component.html b/src/frontend/app/ui/settings/search/search.settings.component.html index 4202e699..31c83665 100644 --- a/src/frontend/app/ui/settings/search/search.settings.component.html +++ b/src/frontend/app/ui/settings/search/search.settings.component.html @@ -52,6 +52,14 @@ [simplifiedMode]="simplifiedMode"> + + + diff --git a/src/frontend/translate/messages.en.xlf b/src/frontend/translate/messages.en.xlf index ab8914c1..f1419dd1 100644 --- a/src/frontend/translate/messages.en.xlf +++ b/src/frontend/translate/messages.en.xlf @@ -2828,6 +2828,14 @@ Shows albums tab in the top bar and enables creating saved searches. Shows albums tab in the top bar and enables creating saved searches. + + List metafiles + List metafiles + + + Search also returns with metafiles from directories that contain a media file of the matched search result + Search also returns with metafiles from directories that contain a media file of the matched search result + \ No newline at end of file diff --git a/src/frontend/translate/messages.es.xlf b/src/frontend/translate/messages.es.xlf index 2e6e3b5e..e69ab912 100644 --- a/src/frontend/translate/messages.es.xlf +++ b/src/frontend/translate/messages.es.xlf @@ -2828,6 +2828,14 @@ Shows albums tab in the top bar and enables creating saved searches. Shows albums tab in the top bar and enables creating saved searches. + + List metafiles + List metafiles + + + Search also returns with metafiles from directories that contain a media file of the matched search result + Search also returns with metafiles from directories that contain a media file of the matched search result + \ No newline at end of file diff --git a/src/frontend/translate/messages.fr.xlf b/src/frontend/translate/messages.fr.xlf index 2ebe30e5..eebbd313 100644 --- a/src/frontend/translate/messages.fr.xlf +++ b/src/frontend/translate/messages.fr.xlf @@ -2828,6 +2828,14 @@ Shows albums tab in the top bar and enables creating saved searches. Shows albums tab in the top bar and enables creating saved searches. + + List metafiles + List metafiles + + + Search also returns with metafiles from directories that contain a media file of the matched search result + Search also returns with metafiles from directories that contain a media file of the matched search result + \ No newline at end of file diff --git a/src/frontend/translate/messages.hu.xlf b/src/frontend/translate/messages.hu.xlf index c105d3d6..8bacaa1e 100644 --- a/src/frontend/translate/messages.hu.xlf +++ b/src/frontend/translate/messages.hu.xlf @@ -2828,6 +2828,14 @@ Shows albums tab in the top bar and enables creating saved searches. Megjeleníti az Album fület a felső sávban és engedélyezi a mentett keresések létrehozásást + + List metafiles + Metafájlok listázása + + + Search also returns with metafiles from directories that contain a media file of the matched search result + A kersési eredmény tartalmazza azokat a metaffájlkoat is, amelyek egy mappában vannak a keresett képpel/videóval. + diff --git a/src/frontend/translate/messages.it.xlf b/src/frontend/translate/messages.it.xlf index 09602755..b42e1c1e 100644 --- a/src/frontend/translate/messages.it.xlf +++ b/src/frontend/translate/messages.it.xlf @@ -2828,6 +2828,14 @@ Shows albums tab in the top bar and enables creating saved searches. Shows albums tab in the top bar and enables creating saved searches. + + List metafiles + List metafiles + + + Search also returns with metafiles from directories that contain a media file of the matched search result + Search also returns with metafiles from directories that contain a media file of the matched search result + \ No newline at end of file diff --git a/src/frontend/translate/messages.pl.xlf b/src/frontend/translate/messages.pl.xlf index 7e0c7f75..88175d7e 100644 --- a/src/frontend/translate/messages.pl.xlf +++ b/src/frontend/translate/messages.pl.xlf @@ -2828,6 +2828,14 @@ Shows albums tab in the top bar and enables creating saved searches. Shows albums tab in the top bar and enables creating saved searches. + + List metafiles + List metafiles + + + Search also returns with metafiles from directories that contain a media file of the matched search result + Search also returns with metafiles from directories that contain a media file of the matched search result + \ No newline at end of file diff --git a/src/frontend/translate/messages.ro.xlf b/src/frontend/translate/messages.ro.xlf index eff30bc6..653b554b 100644 --- a/src/frontend/translate/messages.ro.xlf +++ b/src/frontend/translate/messages.ro.xlf @@ -2828,6 +2828,14 @@ Shows albums tab in the top bar and enables creating saved searches. Shows albums tab in the top bar and enables creating saved searches. + + List metafiles + List metafiles + + + Search also returns with metafiles from directories that contain a media file of the matched search result + Search also returns with metafiles from directories that contain a media file of the matched search result + \ No newline at end of file diff --git a/src/frontend/translate/messages.ru.xlf b/src/frontend/translate/messages.ru.xlf index 25fe8c1d..470ef8a0 100644 --- a/src/frontend/translate/messages.ru.xlf +++ b/src/frontend/translate/messages.ru.xlf @@ -2828,6 +2828,14 @@ Shows albums tab in the top bar and enables creating saved searches. Shows albums tab in the top bar and enables creating saved searches. + + List metafiles + List metafiles + + + Search also returns with metafiles from directories that contain a media file of the matched search result + Search also returns with metafiles from directories that contain a media file of the matched search result + \ No newline at end of file diff --git a/src/frontend/translate/messages.sv.xlf b/src/frontend/translate/messages.sv.xlf index 6834871b..196a3a0f 100644 --- a/src/frontend/translate/messages.sv.xlf +++ b/src/frontend/translate/messages.sv.xlf @@ -2828,6 +2828,14 @@ Shows albums tab in the top bar and enables creating saved searches. Shows albums tab in the top bar and enables creating saved searches. + + List metafiles + List metafiles + + + Search also returns with metafiles from directories that contain a media file of the matched search result + Search also returns with metafiles from directories that contain a media file of the matched search result + \ No newline at end of file diff --git a/test/backend/integration/model/sql/typeorm.ts b/test/backend/integration/model/sql/typeorm.ts index 80d579e2..cbfb2f3a 100644 --- a/test/backend/integration/model/sql/typeorm.ts +++ b/test/backend/integration/model/sql/typeorm.ts @@ -7,16 +7,15 @@ import {UserEntity} from '../../../../../src/backend/model/database/sql/enitites import {UserRoles} from '../../../../../src/common/entities/UserDTO'; import {PasswordHelper} from '../../../../../src/backend/model/PasswordHelper'; import {DirectoryEntity} from '../../../../../src/backend/model/database/sql/enitites/DirectoryEntity'; +import {PhotoEntity, PhotoMetadataEntity} from '../../../../../src/backend/model/database/sql/enitites/PhotoEntity'; import { CameraMetadataEntity, GPSMetadataEntity, - PhotoEntity, - PhotoMetadataEntity, + MediaDimensionEntity, PositionMetaDataEntity -} from '../../../../../src/backend/model/database/sql/enitites/PhotoEntity'; -import {MediaDimensionEntity} from '../../../../../src/backend/model/database/sql/enitites/MediaEntity'; +} from '../../../../../src/backend/model/database/sql/enitites/MediaEntity'; import {VersionEntity} from '../../../../../src/backend/model/database/sql/enitites/VersionEntity'; -import {DatabaseType, ServerConfig} from '../../../../../src/common/config/private/PrivateConfig'; +import {DatabaseType} from '../../../../../src/common/config/private/PrivateConfig'; import {ProjectPath} from '../../../../../src/backend/ProjectPath'; diff --git a/test/backend/unit/model/sql/SearchManager.spec.ts b/test/backend/unit/model/sql/SearchManager.spec.ts index 13974dca..850d1a86 100644 --- a/test/backend/unit/model/sql/SearchManager.spec.ts +++ b/test/backend/unit/model/sql/SearchManager.spec.ts @@ -200,6 +200,9 @@ describe('SearchManager', (sqlHelper: DBTestHelper) => { delete m.directory.preview; delete m.directory.metaFile; const ret = Utils.clone(m); + delete ret.directory.lastScanned; + delete ret.directory.lastModified; + delete ret.directory.mediaCount; if ((ret.metadata as PhotoMetadata).faces && !(ret.metadata as PhotoMetadata).faces.length) { delete (ret.metadata as PhotoMetadata).faces; } diff --git a/test/backend/unit/model/sql/TestHelper.ts b/test/backend/unit/model/sql/TestHelper.ts index e43efbda..94cf628d 100644 --- a/test/backend/unit/model/sql/TestHelper.ts +++ b/test/backend/unit/model/sql/TestHelper.ts @@ -1,11 +1,10 @@ -import {MediaDimensionEntity} from '../../../../../src/backend/model/database/sql/enitites/MediaEntity'; import { CameraMetadataEntity, GPSMetadataEntity, - PhotoEntity, - PhotoMetadataEntity, + MediaDimensionEntity, PositionMetaDataEntity -} from '../../../../../src/backend/model/database/sql/enitites/PhotoEntity'; +} from '../../../../../src/backend/model/database/sql/enitites/MediaEntity'; +import {PhotoEntity, PhotoMetadataEntity} from '../../../../../src/backend/model/database/sql/enitites/PhotoEntity'; import {OrientationTypes} from 'ts-exif-parser'; import {DirectoryEntity} from '../../../../../src/backend/model/database/sql/enitites/DirectoryEntity'; import {VideoEntity, VideoMetadataEntity} from '../../../../../src/backend/model/database/sql/enitites/VideoEntity';