diff --git a/backend/middlewares/AdminMWs.ts b/backend/middlewares/AdminMWs.ts index 92234530b..36ffd644a 100644 --- a/backend/middlewares/AdminMWs.ts +++ b/backend/middlewares/AdminMWs.ts @@ -3,7 +3,7 @@ import {ErrorCodes, ErrorDTO} from '../../common/entities/Error'; import {ObjectManagerRepository} from '../model/ObjectManagerRepository'; import {Logger} from '../Logger'; import {SQLConnection} from '../model/sql/SQLConnection'; -import {DataBaseConfig, DatabaseType, IndexingConfig, IPrivateConfig, ThumbnailConfig} from '../../common/config/private/IPrivateConfig'; +import {DataBaseConfig, DatabaseType, IndexingConfig, ThumbnailConfig} from '../../common/config/private/IPrivateConfig'; import {Config} from '../../common/config/private/Config'; import {ConfigDiagnostics} from '../model/diagnostics/ConfigDiagnostics'; import {ClientConfig} from '../../common/config/public/ConfigClass'; @@ -12,12 +12,36 @@ import {OtherConfigDTO} from '../../common/entities/settings/OtherConfigDTO'; import {ProjectPath} from '../ProjectPath'; import {PrivateConfigClass} from '../../common/config/private/PrivateConfigClass'; import {IndexingDTO} from '../../common/entities/settings/IndexingDTO'; +import {ISQLGalleryManager} from '../model/sql/IGalleryManager'; const LOG_TAG = '[AdminMWs]'; export class AdminMWs { + public static async loadStatistic(req: Request, res: Response, next: NextFunction) { + if (Config.Server.database.type === DatabaseType.memory) { + return next(new ErrorDTO(ErrorCodes.GENERAL_ERROR, 'Statistic is only available for indexed content')); + } + + + const galleryManager = ObjectManagerRepository.getInstance().GalleryManager; + try { + req.resultPipe = { + directories: await galleryManager.countDirectories(), + photos: await galleryManager.countPhotos(), + videos: await galleryManager.countVideos(), + diskUsage: await galleryManager.countMediaSize(), + }; + return next(); + } catch (err) { + if (err instanceof Error) { + return next(new ErrorDTO(ErrorCodes.GENERAL_ERROR, 'Error while getting statistic: ' + err.toString(), err)); + } + return next(new ErrorDTO(ErrorCodes.GENERAL_ERROR, 'Error while getting statistic', err)); + } + } + public static async updateDatabaseSettings(req: Request, res: Response, next: NextFunction) { if ((typeof req.body === 'undefined') || (typeof req.body.settings === 'undefined')) { diff --git a/backend/model/sql/GalleryManager.ts b/backend/model/sql/GalleryManager.ts index a5021e53d..0112ddf1c 100644 --- a/backend/model/sql/GalleryManager.ts +++ b/backend/model/sql/GalleryManager.ts @@ -365,4 +365,35 @@ export class GalleryManager implements IGalleryManager, ISQLGalleryManager { return list; } + async countDirectories(): Promise { + const connection = await SQLConnection.getConnection(); + return await connection.getRepository(DirectoryEntity) + .createQueryBuilder('directory') + .getCount(); + } + + async countMediaSize(): Promise { + const connection = await SQLConnection.getConnection(); + let {sum} = await connection.getRepository(MediaEntity) + .createQueryBuilder('media') + .select('SUM(media.metadata.fileSize)', 'sum') + .getRawOne(); + return sum; + } + + async countPhotos(): Promise { + const connection = await SQLConnection.getConnection(); + return await connection.getRepository(PhotoEntity) + .createQueryBuilder('directory') + .getCount(); + } + + async countVideos(): Promise { + const connection = await SQLConnection.getConnection(); + return await connection.getRepository(VideoEntity) + .createQueryBuilder('directory') + .getCount(); + } + + } diff --git a/backend/model/sql/IGalleryManager.ts b/backend/model/sql/IGalleryManager.ts index d25691788..8667c3593 100644 --- a/backend/model/sql/IGalleryManager.ts +++ b/backend/model/sql/IGalleryManager.ts @@ -8,4 +8,11 @@ export interface ISQLGalleryManager extends IGalleryManager { indexDirectory(relativeDirectoryName: string): Promise; + countDirectories(): Promise; + + countPhotos(): Promise; + + countVideos(): Promise; + + countMediaSize(): Promise; } diff --git a/backend/routes/AdminRouter.ts b/backend/routes/AdminRouter.ts index 83e9963d4..5993c66e4 100644 --- a/backend/routes/AdminRouter.ts +++ b/backend/routes/AdminRouter.ts @@ -7,10 +7,19 @@ import {Express} from 'express'; export class AdminRouter { public static route(app: Express) { + this.addGetStatistic(app); this.addIndexGallery(app); this.addSettings(app); } + private static addGetStatistic(app: Express) { + app.get('/api/admin/statistic', + AuthenticationMWs.authenticate, + AuthenticationMWs.authorise(UserRoles.Admin), + AdminMWs.loadStatistic, + RenderingMWs.renderResult + ); + } private static addIndexGallery(app: Express) { app.get('/api/admin/indexes/job/progress', diff --git a/common/entities/settings/StatisticDTO.ts b/common/entities/settings/StatisticDTO.ts new file mode 100644 index 000000000..8c8b6a5a9 --- /dev/null +++ b/common/entities/settings/StatisticDTO.ts @@ -0,0 +1,6 @@ +export interface StatisticDTO { + directories: number; + photos: number; + videos: number; + diskUsage: number; +} diff --git a/frontend/app/app.module.ts b/frontend/app/app.module.ts index 370bccacf..6c2503711 100644 --- a/frontend/app/app.module.ts +++ b/frontend/app/app.module.ts @@ -73,9 +73,9 @@ import {FixOrientationPipe} from './gallery/FixOrientationPipe'; import {VideoSettingsComponent} from './settings/video/video.settings.component'; import {DurationPipe} from './pipes/DurationPipe'; import {MapService} from './gallery/map/map.service'; -import {Icon} from 'leaflet'; import {MetaFileSettingsComponent} from './settings/metafiles/metafile.settings.component'; import {ThumbnailLoaderService} from './gallery/thumbnailLoader.service'; +import {FileSizePipe} from './pipes/FileSizePipe'; @Injectable() @@ -166,7 +166,8 @@ export function translationsFactory(locale: string) { IconizeSortingMethod, StringifySortingMethod, FixOrientationPipe, - DurationPipe + DurationPipe, + FileSizePipe ], providers: [ {provide: UrlSerializer, useClass: CustomUrlSerializer}, diff --git a/frontend/app/gallery/lightbox/infopanel/info-panel.lightbox.gallery.component.html b/frontend/app/gallery/lightbox/infopanel/info-panel.lightbox.gallery.component.html index 58b17f088..ad7922a34 100644 --- a/frontend/app/gallery/lightbox/infopanel/info-panel.lightbox.gallery.component.html +++ b/frontend/app/gallery/lightbox/infopanel/info-panel.lightbox.gallery.component.html @@ -18,7 +18,7 @@
{{media.metadata.size.width}} x {{media.metadata.size.height}}
{{calcMpx()}}MP
-
{{calcSize(media.metadata.fileSize)}}
+
{{media.metadata.fileSize | fileSize}}
diff --git a/frontend/app/gallery/lightbox/infopanel/info-panel.lightbox.gallery.component.ts b/frontend/app/gallery/lightbox/infopanel/info-panel.lightbox.gallery.component.ts index 5c2f2cacd..de58a2b22 100644 --- a/frontend/app/gallery/lightbox/infopanel/info-panel.lightbox.gallery.component.ts +++ b/frontend/app/gallery/lightbox/infopanel/info-panel.lightbox.gallery.component.ts @@ -40,15 +40,7 @@ export class InfoPanelLightboxComponent { } - calcSize(size: number) { - const postFixes = ['B', 'KB', 'MB', 'GB', 'TB']; - let index = 0; - while (size > 1000 && index < postFixes.length - 1) { - size /= 1000; - index++; - } - return size.toFixed(2) + postFixes[index]; - } + isThisYear() { return (new Date()).getFullYear() === diff --git a/frontend/app/pipes/FileSizePipe.ts b/frontend/app/pipes/FileSizePipe.ts new file mode 100644 index 000000000..de9e99684 --- /dev/null +++ b/frontend/app/pipes/FileSizePipe.ts @@ -0,0 +1,19 @@ +import {Pipe, PipeTransform} from '@angular/core'; +import {I18n} from '@ngx-translate/i18n-polyfill'; + + +@Pipe({name: 'fileSize'}) +export class FileSizePipe implements PipeTransform { + + + transform(size: number): string { + const postFixes = ['B', 'KB', 'MB', 'GB', 'TB']; + let index = 0; + while (size > 1000 && index < postFixes.length - 1) { + size /= 1000; + index++; + } + return size.toFixed(2) + postFixes[index]; + } +} + diff --git a/frontend/app/settings/indexing/indexing.settings.component.css b/frontend/app/settings/indexing/indexing.settings.component.css index e69de29bb..607385027 100644 --- a/frontend/app/settings/indexing/indexing.settings.component.css +++ b/frontend/app/settings/indexing/indexing.settings.component.css @@ -0,0 +1,8 @@ +.buttons-row { + margin-top: 10px; + margin-bottom: 20px; +} + +.statics span.oi { + margin-right: 3px; +} diff --git a/frontend/app/settings/indexing/indexing.settings.component.html b/frontend/app/settings/indexing/indexing.settings.component.html index e6641df80..5de8dd4ac 100644 --- a/frontend/app/settings/indexing/indexing.settings.component.html +++ b/frontend/app/settings/indexing/indexing.settings.component.html @@ -79,31 +79,59 @@
aria-valuemax="100" style="min-width: 2em;" [style.width.%]="(_settingsService.progress.value.indexed/(_settingsService.progress.value.left+_settingsService.progress.value.indexed))*100"> - {{_settingsService.progress.value.indexed}}/{{_settingsService.progress.value.indexed+_settingsService.progress.value.left}} + {{_settingsService.progress.value.indexed}} + /{{_settingsService.progress.value.indexed + _settingsService.progress.value.left}} -
+
+ (click)="index(false)" i18n>Index + + (click)="index(true)" i18n>Index with Thumbnails + + (click)="cancelIndexing()" i18n>Cancel + + (click)="resetDatabase()" i18n>Reset Indexes + +
+
+
+
+ Statistic: +
+
+ + {{statistic ? statistic.directories : '...'}} +
+
+ + {{statistic ? statistic.photos : '...'}} +
+
+ + {{statistic ? statistic.videos : '...'}} + +
+
+ + {{statistic ? (statistic.diskUsage | fileSize) : '...'}} +
diff --git a/frontend/app/settings/indexing/indexing.settings.component.ts b/frontend/app/settings/indexing/indexing.settings.component.ts index af7b3d6bc..0391b77e9 100644 --- a/frontend/app/settings/indexing/indexing.settings.component.ts +++ b/frontend/app/settings/indexing/indexing.settings.component.ts @@ -9,6 +9,7 @@ import {IndexingConfig, ReIndexingSensitivity} from '../../../../common/config/p import {SettingsComponent} from '../_abstract/abstract.settings.component'; import {Utils} from '../../../../common/Utils'; import {I18n} from '@ngx-translate/i18n-polyfill'; +import {StatisticDTO} from '../../../../common/entities/settings/StatisticDTO'; @Component({ selector: 'app-settings-indexing', @@ -26,6 +27,7 @@ export class IndexingSettingsComponent extends SettingsComponent = null; updateProgress = async () => { try { @@ -82,6 +84,7 @@ export class IndexingSettingsComponent extends SettingsComponent { @@ -45,4 +46,7 @@ export class IndexingSettingsService extends AbstractSettingsService('/admin/statistic'); + } } diff --git a/frontend/translate/messages.en.xlf b/frontend/translate/messages.en.xlf index 2b92da991..2aa9f20dd 100644 --- a/frontend/translate/messages.en.xlf +++ b/frontend/translate/messages.en.xlf @@ -1353,11 +1353,12 @@ Note: search only works among the indexed directories - - Index + + Index + app/settings/indexing/indexing.settings.component.html - 93 + 94 Index @@ -1365,15 +1366,16 @@ Indexes the folders app/settings/indexing/indexing.settings.component.html - 91 + 92 Indexes the folders - - Index with Thumbnails + + Index with Thumbnails + app/settings/indexing/indexing.settings.component.html - 99 + 101 Index with Thumbnails @@ -1381,26 +1383,70 @@ Indexes the folders and also creates the thumbnails app/settings/indexing/indexing.settings.component.html - 95 + 97 Indexes the folders and also creates the thumbnails - - Cancel + + Cancel + app/settings/indexing/indexing.settings.component.html - 103 + 106 Cancel - - Reset Indexes + + Reset Indexes + app/settings/indexing/indexing.settings.component.html - 106 + 110 Reset Indexes + + + Statistic: + + + app/settings/indexing/indexing.settings.component.html + 115 + + Statistic: + + + Folders + + app/settings/indexing/indexing.settings.component.html + 119 + + Folders + + + Photos + + app/settings/indexing/indexing.settings.component.html + 123 + + Photos + + + Videos + + app/settings/indexing/indexing.settings.component.html + 127 + + Videos + + + Size + + app/settings/indexing/indexing.settings.component.html + 132 + + Size + Advanced diff --git a/frontend/translate/messages.hu.xlf b/frontend/translate/messages.hu.xlf index 38a15478f..f5b81911c 100644 --- a/frontend/translate/messages.hu.xlf +++ b/frontend/translate/messages.hu.xlf @@ -1353,11 +1353,12 @@ Megjegyzés: csak az indexelt könyvtárak között működik a keresés - - Index + + Index + app/settings/indexing/indexing.settings.component.html - 93 + 94 Index @@ -1365,15 +1366,16 @@ Indexes the folders app/settings/indexing/indexing.settings.component.html - 91 + 92 Indexeli a mappákat - - Index with Thumbnails + + Index with Thumbnails + app/settings/indexing/indexing.settings.component.html - 99 + 101 Index thumbnail-el @@ -1381,26 +1383,70 @@ Indexes the folders and also creates the thumbnails app/settings/indexing/indexing.settings.component.html - 95 + 97 Indexeli a mappákat és legenerálja a thumbnaileket is - - Cancel + + Cancel + app/settings/indexing/indexing.settings.component.html - 103 + 106 Mégse - - Reset Indexes + + Reset Indexes + app/settings/indexing/indexing.settings.component.html - 106 + 110 Indexek visszaállítása + + + Statistic: + + + app/settings/indexing/indexing.settings.component.html + 115 + + Statisztika: + + + Folders + + app/settings/indexing/indexing.settings.component.html + 119 + + Mappák + + + Photos + + app/settings/indexing/indexing.settings.component.html + 123 + + Fotók + + + Videos + + app/settings/indexing/indexing.settings.component.html + 127 + + Videók + + + Size + + app/settings/indexing/indexing.settings.component.html + 132 + + Méret + Advanced