1
0
mirror of https://github.com/bpatrik/pigallery2.git synced 2024-12-23 01:27:14 +02:00

Renaming preview to cover to reduce confusion #679

This commit is contained in:
Patrik J. Braun 2023-08-25 21:24:05 +02:00
parent dd9f804f2c
commit 3c9c7e01a3
33 changed files with 414 additions and 420 deletions

View File

@ -216,8 +216,8 @@ export class ThumbnailGeneratorMWs {
if (typeof directory.media !== 'undefined') {
ThumbnailGeneratorMWs.addThInfoToPhotos(directory.media, directory);
}
if (directory.preview) {
ThumbnailGeneratorMWs.addThInfoToAPhoto(directory.preview, directory);
if (directory.cover) {
ThumbnailGeneratorMWs.addThInfoToAPhoto(directory.cover, directory);
}
}

View File

@ -9,7 +9,7 @@ import {UserManager} from './database/UserManager';
import {IndexingManager} from './database/IndexingManager';
import {SearchManager} from './database/SearchManager';
import {VersionManager} from './database/VersionManager';
import {PreviewManager} from './database/PreviewManager';
import {CoverManager} from './database/CoverManager';
import {AlbumManager} from './database/AlbumManager';
import {PersonManager} from './database/PersonManager';
import {SharingManager} from './database/SharingManager';
@ -27,7 +27,7 @@ export class ObjectManagers {
private sharingManager: SharingManager;
private indexingManager: IndexingManager;
private personManager: PersonManager;
private previewManager: PreviewManager;
private coverManager: CoverManager;
private versionManager: VersionManager;
private jobManager: JobManager;
private locationManager: LocationManager;
@ -85,16 +85,16 @@ export class ObjectManagers {
this.managers.push(this.personManager);
}
get PreviewManager(): PreviewManager {
return this.previewManager;
get CoverManager(): CoverManager {
return this.coverManager;
}
set PreviewManager(value: PreviewManager) {
if (this.previewManager) {
this.managers.splice(this.managers.indexOf(this.previewManager), 1);
set CoverManager(value: CoverManager) {
if (this.coverManager) {
this.managers.splice(this.managers.indexOf(this.coverManager), 1);
}
this.previewManager = value;
this.managers.push(this.previewManager);
this.coverManager = value;
this.managers.push(this.coverManager);
}
get IndexingManager(): IndexingManager {
@ -204,7 +204,7 @@ export class ObjectManagers {
ObjectManagers.getInstance().GalleryManager = new GalleryManager();
ObjectManagers.getInstance().IndexingManager = new IndexingManager();
ObjectManagers.getInstance().PersonManager = new PersonManager();
ObjectManagers.getInstance().PreviewManager = new PreviewManager();
ObjectManagers.getInstance().CoverManager = new CoverManager();
ObjectManagers.getInstance().SearchManager = new SearchManager();
ObjectManagers.getInstance().SharingManager = new SharingManager();
ObjectManagers.getInstance().UserManager = new UserManager();

View File

@ -19,15 +19,15 @@ export class AlbumManager implements IObjectManager {
private static async updateAlbum(album: SavedSearchEntity): Promise<void> {
const connection = await SQLConnection.getConnection();
const preview =
await ObjectManagers.getInstance().PreviewManager.getAlbumPreview(album);
const cover =
await ObjectManagers.getInstance().CoverManager.getAlbumCover(album);
const count = await
ObjectManagers.getInstance().SearchManager.getCount((album as SavedSearchDTO).searchQuery);
await connection
.createQueryBuilder()
.update(AlbumBaseEntity)
.set({preview, count})
.set({cover: cover, count})
.where('id = :id', {id: album.id})
.execute();
}
@ -81,17 +81,17 @@ export class AlbumManager implements IObjectManager {
return await connection
.getRepository(AlbumBaseEntity)
.createQueryBuilder('album')
.leftJoin('album.preview', 'preview')
.leftJoin('preview.directory', 'directory')
.select(['album', 'preview.name', 'directory.name', 'directory.path'])
.leftJoin('album.cover', 'cover')
.leftJoin('cover.directory', 'directory')
.select(['album', 'cover.name', 'directory.name', 'directory.path'])
.getMany();
}
public async onNewDataVersion(): Promise<void> {
await this.resetPreviews();
await this.resetCovers();
}
public async resetPreviews(): Promise<void> {
public async resetCovers(): Promise<void> {
this.isDBValid = false;
}

View File

@ -11,24 +11,24 @@ import {DirectoryEntity} from './enitites/DirectoryEntity';
import {ParentDirectoryDTO} from '../../../common/entities/DirectoryDTO';
import * as path from 'path';
import {Utils} from '../../../common/Utils';
import {PreviewPhotoDTO} from '../../../common/entities/PhotoDTO';
import {CoverPhotoDTO} from '../../../common/entities/PhotoDTO';
import {IObjectManager} from './IObjectManager';
import {Logger} from '../../Logger';
const LOG_TAG = '[PreviewManager]';
const LOG_TAG = '[CoverManager]';
// ID is need within the backend so it can be saved to DB (ID is the external key)
export interface PreviewPhotoDTOWithID extends PreviewPhotoDTO {
export interface CoverPhotoDTOWithID extends CoverPhotoDTO {
id: number;
}
export class PreviewManager implements IObjectManager {
export class CoverManager implements IObjectManager {
private static DIRECTORY_SELECT = ['directory.name', 'directory.path'];
private static setSorting<T>(
query: SelectQueryBuilder<T>
): SelectQueryBuilder<T> {
for (const sort of Config.Preview.Sorting) {
for (const sort of Config.AlbumCover.Sorting) {
switch (sort) {
case SortingMethods.descDate:
query.addOrderBy('media.metadata.creationDate', 'DESC');
@ -54,24 +54,24 @@ export class PreviewManager implements IObjectManager {
return query;
}
public async resetPreviews(): Promise<void> {
public async resetCovers(): Promise<void> {
const connection = await SQLConnection.getConnection();
await connection
.createQueryBuilder()
.update(DirectoryEntity)
.set({validPreview: false})
.set({validCover: false})
.execute();
}
public async onNewDataVersion(changedDir: ParentDirectoryDTO): Promise<void> {
// Invalidating Album preview
// Invalidating Album cover
let fullPath = DiskMangerWorker.normalizeDirPath(
path.join(changedDir.path, changedDir.name)
);
const query = (await SQLConnection.getConnection())
.createQueryBuilder()
.update(DirectoryEntity)
.set({validPreview: false});
.set({validCover: false});
let i = 0;
const root = DiskMangerWorker.pathFromRelativeDirName('.');
@ -105,79 +105,79 @@ export class PreviewManager implements IObjectManager {
await query.execute();
}
public async getAlbumPreview(album: {
public async getAlbumCover(album: {
searchQuery: SearchQueryDTO;
}): Promise<PreviewPhotoDTOWithID> {
}): Promise<CoverPhotoDTOWithID> {
const albumQuery: Brackets = await
ObjectManagers.getInstance().SearchManager.prepareAndBuildWhereQuery(album.searchQuery);
const connection = await SQLConnection.getConnection();
const previewQuery = (): SelectQueryBuilder<MediaEntity> => {
const coverQuery = (): SelectQueryBuilder<MediaEntity> => {
const query = connection
.getRepository(MediaEntity)
.createQueryBuilder('media')
.innerJoin('media.directory', 'directory')
.select(['media.name', 'media.id', ...PreviewManager.DIRECTORY_SELECT])
.select(['media.name', 'media.id', ...CoverManager.DIRECTORY_SELECT])
.where(albumQuery);
PreviewManager.setSorting(query);
CoverManager.setSorting(query);
return query;
};
let previewMedia = null;
let coverMedia = null;
if (
Config.Preview.SearchQuery &&
!Utils.equalsFilter(Config.Preview.SearchQuery, {
Config.AlbumCover.SearchQuery &&
!Utils.equalsFilter(Config.AlbumCover.SearchQuery, {
type: SearchQueryTypes.any_text,
text: '',
} as TextSearch)
) {
try {
const previewFilterQuery = await
ObjectManagers.getInstance().SearchManager.prepareAndBuildWhereQuery(Config.Preview.SearchQuery);
previewMedia = await previewQuery()
.andWhere(previewFilterQuery)
const coverFilterQuery = await
ObjectManagers.getInstance().SearchManager.prepareAndBuildWhereQuery(Config.AlbumCover.SearchQuery);
coverMedia = await coverQuery()
.andWhere(coverFilterQuery)
.limit(1)
.getOne();
} catch (e) {
Logger.error('Cant get album preview using:', JSON.stringify(album.searchQuery), JSON.stringify(Config.Preview.SearchQuery));
Logger.error('Cant get album cover using:', JSON.stringify(album.searchQuery), JSON.stringify(Config.AlbumCover.SearchQuery));
throw e;
}
}
if (!previewMedia) {
if (!coverMedia) {
try {
previewMedia = await previewQuery().limit(1).getOne();
coverMedia = await coverQuery().limit(1).getOne();
} catch (e) {
Logger.error('Cant get album preview using:', JSON.stringify(album.searchQuery));
Logger.error('Cant get album cover using:', JSON.stringify(album.searchQuery));
throw e;
}
}
return previewMedia || null;
return coverMedia || null;
}
public async getPartialDirsWithoutPreviews(): Promise<
public async getPartialDirsWithoutCovers(): Promise<
{ id: number; name: string; path: string }[]
> {
const connection = await SQLConnection.getConnection();
return await connection
.getRepository(DirectoryEntity)
.createQueryBuilder('directory')
.where('directory.validPreview = :validPreview', {validPreview: 0}) // 0 === false
.where('directory.validCover = :validCover', {validCover: 0}) // 0 === false
.select(['name', 'id', 'path'])
.getRawMany();
}
public async setAndGetPreviewForDirectory(dir: {
public async setAndGetCoverForDirectory(dir: {
id: number;
name: string;
path: string;
}): Promise<PreviewPhotoDTOWithID> {
}): Promise<CoverPhotoDTOWithID> {
const connection = await SQLConnection.getConnection();
const previewQuery = (): SelectQueryBuilder<MediaEntity> => {
const coverQuery = (): SelectQueryBuilder<MediaEntity> => {
const query = connection
.getRepository(MediaEntity)
.createQueryBuilder('media')
.innerJoin('media.directory', 'directory')
.select(['media.name', 'media.id', ...PreviewManager.DIRECTORY_SELECT])
.select(['media.name', 'media.id', ...CoverManager.DIRECTORY_SELECT])
.where(
new Brackets((q: WhereExpression) => {
q.where('media.directory = :dir', {
@ -201,40 +201,40 @@ export class PreviewManager implements IObjectManager {
'ASC'
);
PreviewManager.setSorting(query);
CoverManager.setSorting(query);
return query;
};
let previewMedia: PreviewPhotoDTOWithID = null;
let coverMedia: CoverPhotoDTOWithID = null;
if (
Config.Preview.SearchQuery &&
!Utils.equalsFilter(Config.Preview.SearchQuery, {
Config.AlbumCover.SearchQuery &&
!Utils.equalsFilter(Config.AlbumCover.SearchQuery, {
type: SearchQueryTypes.any_text,
text: '',
} as TextSearch)
) {
previewMedia = await previewQuery()
coverMedia = await coverQuery()
.andWhere(
await ObjectManagers.getInstance().SearchManager.prepareAndBuildWhereQuery(Config.Preview.SearchQuery)
await ObjectManagers.getInstance().SearchManager.prepareAndBuildWhereQuery(Config.AlbumCover.SearchQuery)
)
.limit(1)
.getOne();
}
if (!previewMedia) {
previewMedia = await previewQuery().limit(1).getOne();
if (!coverMedia) {
coverMedia = await coverQuery().limit(1).getOne();
}
// set validPreview bit to true even if there is no preview (to prevent future updates)
// set validCover bit to true even if there is no cover (to prevent future updates)
await connection
.createQueryBuilder()
.update(DirectoryEntity)
.set({preview: previewMedia, validPreview: true})
.set({cover: coverMedia, validCover: true})
.where('id = :dir', {
dir: dir.id,
})
.execute();
return previewMedia || null;
return coverMedia || null;
}
}

View File

@ -86,9 +86,9 @@ export class GalleryManager {
relativeDirectoryName
);
for (const subDir of ret.directories) {
if (!subDir.preview) {
// if subdirectories do not have photos, so cannot show a preview, try getting one from DB
await this.fillPreviewForSubDir(connection, subDir);
if (!subDir.cover) {
// if subdirectories do not have photos, so cannot show a cover, try getting one from DB
await this.fillCoverForSubDir(connection, subDir);
}
}
return ret;
@ -292,15 +292,15 @@ export class GalleryManager {
}
/**
* Sets preview for the directory and caches it in the DB
* Sets cover for the directory and caches it in the DB
*/
public async fillPreviewForSubDir(
public async fillCoverForSubDir(
connection: Connection,
dir: SubDirectoryDTO
): Promise<void> {
if (!dir.validPreview) {
dir.preview =
await ObjectManagers.getInstance().PreviewManager.setAndGetPreviewForDirectory(
if (!dir.validCover) {
dir.cover =
await ObjectManagers.getInstance().CoverManager.setAndGetCoverForDirectory(
dir
);
}
@ -336,15 +336,15 @@ export class GalleryManager {
})
.leftJoinAndSelect('directory.directories', 'directories')
.leftJoinAndSelect('directory.media', 'media')
.leftJoinAndSelect('directories.preview', 'preview')
.leftJoinAndSelect('preview.directory', 'previewDirectory')
.leftJoinAndSelect('directories.cover', 'cover')
.leftJoinAndSelect('cover.directory', 'coverDirectory')
.select([
'directory',
'directories',
'media',
'preview.name',
'previewDirectory.name',
'previewDirectory.path',
'cover.name',
'coverDirectory.name',
'coverDirectory.path',
]);
// TODO: do better filtering
@ -360,7 +360,7 @@ export class GalleryManager {
const dir = await query.getOne();
if (dir.directories) {
for (const item of dir.directories) {
await this.fillPreviewForSubDir(connection, item);
await this.fillCoverForSubDir(connection, item);
}
}

View File

@ -320,21 +320,21 @@ export class SearchManager {
.getRepository(DirectoryEntity)
.createQueryBuilder('directory')
.where(this.buildWhereQuery(dirQuery, true))
.leftJoinAndSelect('directory.preview', 'preview')
.leftJoinAndSelect('preview.directory', 'previewDirectory')
.leftJoinAndSelect('directory.cover', 'cover')
.leftJoinAndSelect('cover.directory', 'coverDirectory')
.limit(Config.Search.maxDirectoryResult + 1)
.select([
'directory',
'preview.name',
'previewDirectory.name',
'previewDirectory.path',
'cover.name',
'coverDirectory.name',
'coverDirectory.path',
])
.getMany();
// setting previews
// setting covers
if (result.directories) {
for (const item of result.directories) {
await ObjectManagers.getInstance().GalleryManager.fillPreviewForSubDir(connection, item as DirectoryEntity);
await ObjectManagers.getInstance().GalleryManager.fillCoverForSubDir(connection, item as DirectoryEntity);
}
}
if (

View File

@ -46,7 +46,7 @@ export class DirectoryEntity
public lastModified: number;
/**
* Last time the directory was fully scanned, not only for a few media to create a preview
* Last time the directory was fully scanned, not only for a few media to create a cover
*/
@Column({
type: 'bigint',
@ -75,11 +75,11 @@ export class DirectoryEntity
// not saving to database, it is only assigned when querying the DB
@ManyToOne((type) => MediaEntity, { onDelete: 'SET NULL' })
public preview: MediaEntity;
public cover: MediaEntity;
// On galley change, preview will be invalid
// On galley change, cover will be invalid
@Column({ type: 'boolean', default: false })
validPreview: boolean;
validCover: boolean;
@OneToMany((type) => MediaEntity, (media) => media.directory)
public media: MediaEntity[];

View File

@ -24,5 +24,5 @@ export class AlbumBaseEntity implements AlbumBaseDTO {
count: number;
@ManyToOne((type) => MediaEntity, {onDelete: 'SET NULL', nullable: true})
public preview: MediaEntity;
public cover: MediaEntity;
}

View File

@ -17,9 +17,9 @@ import {
} from '../../../common/config/public/ClientConfig';
import {
DatabaseType,
ServerAlbumCoverConfig,
ServerDataBaseConfig,
ServerJobConfig,
ServerPreviewConfig,
ServerThumbnailConfig,
ServerVideoConfig,
} from '../../../common/config/private/PrivateConfig';
@ -31,8 +31,8 @@ const LOG_TAG = '[ConfigDiagnostics]';
export class ConfigDiagnostics {
static testAlbumsConfig(
albumConfig: ClientAlbumConfig,
original: PrivateConfigClass
albumConfig: ClientAlbumConfig,
original: PrivateConfigClass
): void {
Logger.debug(LOG_TAG, 'Testing album config');
// nothing to check
@ -51,27 +51,27 @@ export class ConfigDiagnostics {
}
static async testDatabase(
databaseConfig: ServerDataBaseConfig
databaseConfig: ServerDataBaseConfig
): Promise<void> {
Logger.debug(LOG_TAG, 'Testing database config');
await SQLConnection.tryConnection(databaseConfig);
if (databaseConfig.type === DatabaseType.sqlite) {
try {
await this.checkReadWritePermission(
SQLConnection.getSQLiteDB(databaseConfig)
SQLConnection.getSQLiteDB(databaseConfig)
);
} catch (e) {
throw new Error(
'Cannot read or write sqlite storage file: ' +
SQLConnection.getSQLiteDB(databaseConfig)
'Cannot read or write sqlite storage file: ' +
SQLConnection.getSQLiteDB(databaseConfig)
);
}
}
}
static async testMetaFileConfig(
metaFileConfig: ClientMetaFileConfig,
config: PrivateConfigClass
metaFileConfig: ClientMetaFileConfig,
config: PrivateConfigClass
): Promise<void> {
Logger.debug(LOG_TAG, 'Testing meta file config');
if (metaFileConfig.gpx === true && config.Map.enabled === false) {
@ -96,19 +96,19 @@ export class ConfigDiagnostics {
ffmpeg().getAvailableCodecs((err: Error) => {
if (err) {
return reject(
new Error(
'Error accessing ffmpeg, cant find executable: ' +
err.toString()
)
new Error(
'Error accessing ffmpeg, cant find executable: ' +
err.toString()
)
);
}
ffmpeg(__dirname + '/blank.jpg').ffprobe((err2: Error) => {
if (err2) {
return reject(
new Error(
'Error accessing ffmpeg-probe, cant find executable: ' +
err2.toString()
)
new Error(
'Error accessing ffmpeg-probe, cant find executable: ' +
err2.toString()
)
);
}
return resolve();
@ -156,7 +156,7 @@ export class ConfigDiagnostics {
static async testThumbnailConfig(
thumbnailConfig: ServerThumbnailConfig
thumbnailConfig: ServerThumbnailConfig
): Promise<void> {
Logger.debug(LOG_TAG, 'Testing thumbnail config');
@ -167,7 +167,7 @@ export class ConfigDiagnostics {
if (isNaN(thumbnailConfig.iconSize) || thumbnailConfig.iconSize <= 0) {
throw new Error(
'IconSize has to be >= 0 integer, got: ' + thumbnailConfig.iconSize
'IconSize has to be >= 0 integer, got: ' + thumbnailConfig.iconSize
);
}
@ -182,16 +182,16 @@ export class ConfigDiagnostics {
}
static async testTasksConfig(
task: ServerJobConfig,
config: PrivateConfigClass
task: ServerJobConfig,
config: PrivateConfigClass
): Promise<void> {
Logger.debug(LOG_TAG, 'Testing tasks config');
return;
}
static async testFacesConfig(
faces: ClientFacesConfig,
config: PrivateConfigClass
faces: ClientFacesConfig,
config: PrivateConfigClass
): Promise<void> {
Logger.debug(LOG_TAG, 'Testing faces config');
if (faces.enabled === true) {
@ -202,29 +202,29 @@ export class ConfigDiagnostics {
}
static async testSearchConfig(
search: ClientSearchConfig,
config: PrivateConfigClass
search: ClientSearchConfig,
config: PrivateConfigClass
): Promise<void> {
Logger.debug(LOG_TAG, 'Testing search config');
//nothing to check
}
static async testSharingConfig(
sharing: ClientSharingConfig,
config: PrivateConfigClass
sharing: ClientSharingConfig,
config: PrivateConfigClass
): Promise<void> {
Logger.debug(LOG_TAG, 'Testing sharing config');
if (
sharing.enabled === true &&
config.Users.authenticationRequired === false
sharing.enabled === true &&
config.Users.authenticationRequired === false
) {
throw new Error('In case of no authentication, sharing is not supported');
}
}
static async testRandomPhotoConfig(
sharing: ClientRandomPhotoConfig,
config: PrivateConfigClass
sharing: ClientRandomPhotoConfig,
config: PrivateConfigClass
): Promise<void> {
Logger.debug(LOG_TAG, 'Testing random photo config');
//nothing to check
@ -236,14 +236,14 @@ export class ConfigDiagnostics {
return;
}
if (
map.mapProvider === MapProviders.Mapbox &&
(!map.mapboxAccessToken || map.mapboxAccessToken.length === 0)
map.mapProvider === MapProviders.Mapbox &&
(!map.mapboxAccessToken || map.mapboxAccessToken.length === 0)
) {
throw new Error('Mapbox needs a valid api key.');
}
if (
map.mapProvider === MapProviders.Custom &&
(!map.customLayers || map.customLayers.length === 0)
map.mapProvider === MapProviders.Custom &&
(!map.customLayers || map.customLayers.length === 0)
) {
throw new Error('Custom maps need at least one valid layer');
}
@ -256,14 +256,14 @@ export class ConfigDiagnostics {
}
}
static async testPreviewConfig(settings: ServerPreviewConfig): Promise<void> {
Logger.debug(LOG_TAG, 'Testing preview config');
static async testAlbumCoverConfig(settings: ServerAlbumCoverConfig): Promise<void> {
Logger.debug(LOG_TAG, 'Testing cover config');
const sp = new SearchQueryParser();
if (
!Utils.equalsFilter(
sp.parse(sp.stringify(settings.SearchQuery)),
settings.SearchQuery
)
!Utils.equalsFilter(
sp.parse(sp.stringify(settings.SearchQuery)),
settings.SearchQuery
)
) {
throw new Error('SearchQuery is not valid. Got: ' + JSON.stringify(sp.parse(sp.stringify(settings.SearchQuery))));
}
@ -280,7 +280,7 @@ export class ConfigDiagnostics {
await ConfigDiagnostics.testImageFolder(config.Media.folder);
await ConfigDiagnostics.testThumbnailConfig(config.Media.Thumbnail);
await ConfigDiagnostics.testSearchConfig(config.Search, config);
await ConfigDiagnostics.testPreviewConfig(config.Preview);
await ConfigDiagnostics.testAlbumCoverConfig(config.AlbumCover);
await ConfigDiagnostics.testFacesConfig(config.Faces, config);
await ConfigDiagnostics.testTasksConfig(config.Jobs, config);
await ConfigDiagnostics.testSharingConfig(config.Sharing, config);
@ -303,8 +303,8 @@ export class ConfigDiagnostics {
const err: Error = ex;
Logger.warn(LOG_TAG, '[SQL error]', err.toString());
Logger.error(
LOG_TAG,
'Error during initializing SQL DB, check DB connection and settings'
LOG_TAG,
'Error during initializing SQL DB, check DB connection and settings'
);
process.exit(1);
}
@ -315,15 +315,15 @@ export class ConfigDiagnostics {
const err: Error = ex;
Logger.warn(
LOG_TAG,
'[Thumbnail hardware acceleration] module error: ',
err.toString()
LOG_TAG,
'[Thumbnail hardware acceleration] module error: ',
err.toString()
);
Logger.warn(
LOG_TAG,
'Thumbnail hardware acceleration is not possible.' +
' \'sharp\' node module is not found.' +
' Falling back temporally to JS based thumbnail generation'
LOG_TAG,
'Thumbnail hardware acceleration is not possible.' +
' \'sharp\' node module is not found.' +
' Falling back temporally to JS based thumbnail generation'
);
process.exit(1);
}
@ -341,32 +341,32 @@ export class ConfigDiagnostics {
} catch (ex) {
const err: Error = ex;
NotificationManager.warning(
'Video support error, switching off..',
err.toString()
'Video support error, switching off..',
err.toString()
);
Logger.warn(
LOG_TAG,
'Video support error, switching off..',
err.toString()
LOG_TAG,
'Video support error, switching off..',
err.toString()
);
Config.Media.Video.enabled = false;
}
try {
await ConfigDiagnostics.testMetaFileConfig(
Config.MetaFile,
Config
Config.MetaFile,
Config
);
} catch (ex) {
const err: Error = ex;
NotificationManager.warning(
'Meta file support error, switching off gpx..',
err.toString()
'Meta file support error, switching off gpx..',
err.toString()
);
Logger.warn(
LOG_TAG,
'Meta file support error, switching off..',
err.toString()
LOG_TAG,
'Meta file support error, switching off..',
err.toString()
);
Config.MetaFile.gpx = false;
}
@ -376,13 +376,13 @@ export class ConfigDiagnostics {
} catch (ex) {
const err: Error = ex;
NotificationManager.warning(
'Albums support error, switching off..',
err.toString()
'Albums support error, switching off..',
err.toString()
);
Logger.warn(
LOG_TAG,
'Meta file support error, switching off..',
err.toString()
LOG_TAG,
'Meta file support error, switching off..',
err.toString()
);
Config.Album.enabled = false;
}
@ -396,7 +396,7 @@ export class ConfigDiagnostics {
}
try {
await ConfigDiagnostics.testThumbnailConfig(
Config.Media.Thumbnail
Config.Media.Thumbnail
);
} catch (ex) {
const err: Error = ex;
@ -409,32 +409,32 @@ export class ConfigDiagnostics {
} catch (ex) {
const err: Error = ex;
NotificationManager.warning(
'Search is not supported with these settings. Disabling temporally. ' +
'Please adjust the config properly.',
err.toString()
'Search is not supported with these settings. Disabling temporally. ' +
'Please adjust the config properly.',
err.toString()
);
Logger.warn(
LOG_TAG,
'Search is not supported with these settings, switching off..',
err.toString()
LOG_TAG,
'Search is not supported with these settings, switching off..',
err.toString()
);
Config.Search.enabled = false;
}
try {
await ConfigDiagnostics.testPreviewConfig(Config.Preview);
await ConfigDiagnostics.testAlbumCoverConfig(Config.AlbumCover);
} catch (ex) {
const err: Error = ex;
NotificationManager.warning(
'Preview settings are not valid, resetting search query',
err.toString()
'Cover settings are not valid, resetting search query',
err.toString()
);
Logger.warn(
LOG_TAG,
'Preview settings are not valid, resetting search query',
err.toString()
LOG_TAG,
'Cover settings are not valid, resetting search query',
err.toString()
);
Config.Preview.SearchQuery = {
Config.AlbumCover.SearchQuery = {
type: SearchQueryTypes.any_text,
text: '',
} as TextSearch;
@ -445,14 +445,14 @@ export class ConfigDiagnostics {
} catch (ex) {
const err: Error = ex;
NotificationManager.warning(
'Faces are not supported with these settings. Disabling temporally. ' +
'Please adjust the config properly.',
err.toString()
'Faces are not supported with these settings. Disabling temporally. ' +
'Please adjust the config properly.',
err.toString()
);
Logger.warn(
LOG_TAG,
'Faces are not supported with these settings, switching off..',
err.toString()
LOG_TAG,
'Faces are not supported with these settings, switching off..',
err.toString()
);
Config.Faces.enabled = false;
}
@ -462,14 +462,14 @@ export class ConfigDiagnostics {
} catch (ex) {
const err: Error = ex;
NotificationManager.warning(
'Some Tasks are not supported with these settings. Disabling temporally. ' +
'Please adjust the config properly.',
err.toString()
'Some Tasks are not supported with these settings. Disabling temporally. ' +
'Please adjust the config properly.',
err.toString()
);
Logger.warn(
LOG_TAG,
'Some Tasks not supported with these settings, switching off..',
err.toString()
LOG_TAG,
'Some Tasks not supported with these settings, switching off..',
err.toString()
);
Config.Faces.enabled = false;
}
@ -479,34 +479,34 @@ export class ConfigDiagnostics {
} catch (ex) {
const err: Error = ex;
NotificationManager.warning(
'Sharing is not supported with these settings. Disabling temporally. ' +
'Please adjust the config properly.',
err.toString()
'Sharing is not supported with these settings. Disabling temporally. ' +
'Please adjust the config properly.',
err.toString()
);
Logger.warn(
LOG_TAG,
'Sharing is not supported with these settings, switching off..',
err.toString()
LOG_TAG,
'Sharing is not supported with these settings, switching off..',
err.toString()
);
Config.Sharing.enabled = false;
}
try {
await ConfigDiagnostics.testRandomPhotoConfig(
Config.Sharing,
Config
Config.Sharing,
Config
);
} catch (ex) {
const err: Error = ex;
NotificationManager.warning(
'Random Media is not supported with these settings. Disabling temporally. ' +
'Please adjust the config properly.',
err.toString()
'Random Media is not supported with these settings. Disabling temporally. ' +
'Please adjust the config properly.',
err.toString()
);
Logger.warn(
LOG_TAG,
'Random Media is not supported with these settings, switching off..',
err.toString()
LOG_TAG,
'Random Media is not supported with these settings, switching off..',
err.toString()
);
Config.Sharing.enabled = false;
}
@ -516,15 +516,15 @@ export class ConfigDiagnostics {
} catch (ex) {
const err: Error = ex;
NotificationManager.warning(
'Maps is not supported with these settings. Using open street maps temporally. ' +
'Please adjust the config properly.',
err.toString()
'Maps is not supported with these settings. Using open street maps temporally. ' +
'Please adjust the config properly.',
err.toString()
);
Logger.warn(
LOG_TAG,
'Maps is not supported with these settings. Using open street maps temporally ' +
'Please adjust the config properly.',
err.toString()
LOG_TAG,
'Maps is not supported with these settings. Using open street maps temporally ' +
'Please adjust the config properly.',
err.toString()
);
Config.Map.mapProvider = MapProviders.OpenStreetMap;
}

View File

@ -5,12 +5,12 @@ import {VideoConvertingJob} from './jobs/VideoConvertingJob';
import {PhotoConvertingJob} from './jobs/PhotoConvertingJob';
import {ThumbnailGenerationJob} from './jobs/ThumbnailGenerationJob';
import {TempFolderCleaningJob} from './jobs/TempFolderCleaningJob';
import {PreviewFillingJob} from './jobs/PreviewFillingJob';
import {PreviewRestJob} from './jobs/PreviewResetJob';
import {AlbumCoverFillingJob} from './jobs/AlbumCoverFillingJob';
import {GPXCompressionJob} from './jobs/GPXCompressionJob';
import {AlbumRestJob} from './jobs/AlbumResetJob';
import {GPXCompressionResetJob} from './jobs/GPXCompressionResetJob';
import {TopPickSendJob} from './jobs/TopPickSendJob';
import {AlbumCoverRestJob} from './jobs/AlbumCoverResetJob';
export class JobRepository {
private static instance: JobRepository = null;
@ -37,8 +37,8 @@ export class JobRepository {
JobRepository.Instance.register(new IndexingJob());
JobRepository.Instance.register(new GalleryRestJob());
JobRepository.Instance.register(new PreviewFillingJob());
JobRepository.Instance.register(new PreviewRestJob());
JobRepository.Instance.register(new AlbumCoverFillingJob());
JobRepository.Instance.register(new AlbumCoverRestJob());
JobRepository.Instance.register(new VideoConvertingJob());
JobRepository.Instance.register(new PhotoConvertingJob());
JobRepository.Instance.register(new ThumbnailGenerationJob());

View File

@ -2,10 +2,10 @@ import {ObjectManagers} from '../../ObjectManagers';
import {ConfigTemplateEntry, DefaultsJobs,} from '../../../../common/entities/job/JobDTO';
import {Job} from './Job';
export class PreviewFillingJob extends Job {
public readonly Name = DefaultsJobs[DefaultsJobs['Preview Filling']];
export class AlbumCoverFillingJob extends Job {
public readonly Name = DefaultsJobs[DefaultsJobs['Album Cover Filling']];
public readonly ConfigTemplate: ConfigTemplateEntry[] = null;
directoryToSetPreview: { id: number; name: string; path: string }[] = null;
directoryToSetCover: { id: number; name: string; path: string }[] = null;
status: 'Persons' | 'Albums' | 'Directory' = 'Persons';
public get Supported(): boolean {
@ -17,11 +17,11 @@ export class PreviewFillingJob extends Job {
}
protected async step(): Promise<boolean> {
if (!this.directoryToSetPreview) {
if (!this.directoryToSetCover) {
this.Progress.log('Loading Directories to process');
this.directoryToSetPreview =
await ObjectManagers.getInstance().PreviewManager.getPartialDirsWithoutPreviews();
this.Progress.Left = this.directoryToSetPreview.length + 2;
this.directoryToSetCover =
await ObjectManagers.getInstance().CoverManager.getPartialDirsWithoutCovers();
this.Progress.Left = this.directoryToSetCover.length + 2;
return true;
}
@ -31,18 +31,18 @@ export class PreviewFillingJob extends Job {
this.status = 'Albums';
return true;
case 'Albums':
await this.stepAlbumPreview();
await this.stepAlbumCover();
this.status = 'Directory';
return true;
case 'Directory':
return await this.stepDirectoryPreview();
return await this.stepDirectoryCover();
}
return false;
}
private async stepAlbumPreview(): Promise<boolean> {
private async stepAlbumCover(): Promise<boolean> {
await ObjectManagers.getInstance().AlbumManager.getAlbums();
this.Progress.log('Updating Albums preview');
this.Progress.log('Updating Albums cover');
this.Progress.Processed++;
return false;
}
@ -54,22 +54,22 @@ export class PreviewFillingJob extends Job {
return false;
}
private async stepDirectoryPreview(): Promise<boolean> {
if (this.directoryToSetPreview.length === 0) {
this.directoryToSetPreview =
await ObjectManagers.getInstance().PreviewManager.getPartialDirsWithoutPreviews();
private async stepDirectoryCover(): Promise<boolean> {
if (this.directoryToSetCover.length === 0) {
this.directoryToSetCover =
await ObjectManagers.getInstance().CoverManager.getPartialDirsWithoutCovers();
// double check if there is really no more
if (this.directoryToSetPreview.length > 0) {
if (this.directoryToSetCover.length > 0) {
return true; // continue
}
this.Progress.Left = 0;
return false;
}
const directory = this.directoryToSetPreview.shift();
this.Progress.log('Setting preview: ' + directory.path + directory.name);
this.Progress.Left = this.directoryToSetPreview.length;
const directory = this.directoryToSetCover.shift();
this.Progress.log('Setting cover: ' + directory.path + directory.name);
this.Progress.Left = this.directoryToSetCover.length;
await ObjectManagers.getInstance().PreviewManager.setAndGetPreviewForDirectory(
await ObjectManagers.getInstance().CoverManager.setAndGetCoverForDirectory(
directory
);
this.Progress.Processed++;

View File

@ -0,0 +1,26 @@
import {ObjectManagers} from '../../ObjectManagers';
import {ConfigTemplateEntry, DefaultsJobs,} from '../../../../common/entities/job/JobDTO';
import {Job} from './Job';
export class AlbumCoverRestJob extends Job {
public readonly Name = DefaultsJobs[DefaultsJobs['Album Cover Reset']];
public readonly ConfigTemplate: ConfigTemplateEntry[] = null;
protected readonly IsInstant = true;
public get Supported(): boolean {
return true;
}
protected async init(): Promise<void> {
// abstract function
}
protected async step(): Promise<boolean> {
this.Progress.Left = 1;
this.Progress.Processed++;
await ObjectManagers.getInstance().CoverManager.resetCovers();
await ObjectManagers.getInstance().AlbumManager.resetCovers();
await ObjectManagers.getInstance().PersonManager.resetPreviews();
return false;
}
}

View File

@ -1,31 +0,0 @@
import { ObjectManagers } from '../../ObjectManagers';
import { Config } from '../../../../common/config/private/Config';
import {
ConfigTemplateEntry,
DefaultsJobs,
} from '../../../../common/entities/job/JobDTO';
import { Job } from './Job';
import { DatabaseType } from '../../../../common/config/private/PrivateConfig';
export class PreviewRestJob extends Job {
public readonly Name = DefaultsJobs[DefaultsJobs['Preview Reset']];
public readonly ConfigTemplate: ConfigTemplateEntry[] = null;
protected readonly IsInstant = true;
public get Supported(): boolean {
return true;
}
protected async init(): Promise<void> {
// abstract function
}
protected async step(): Promise<boolean> {
this.Progress.Left = 1;
this.Progress.Processed++;
await ObjectManagers.getInstance().PreviewManager.resetPreviews();
await ObjectManagers.getInstance().AlbumManager.resetPreviews();
await ObjectManagers.getInstance().PersonManager.resetPreviews();
return false;
}
}

View File

@ -124,8 +124,8 @@ export class DiskMangerWorker {
directories: [],
isPartial: false,
mediaCount: 0,
preview: null,
validPreview: false,
cover: null,
validCover: false,
media: [],
metaFile: [],
};
@ -146,7 +146,7 @@ export class DiskMangerWorker {
if ((await fsp.stat(fullFilePath)).isDirectory()) {
if (
settings.noDirectory === true ||
settings.previewOnly === true ||
settings.coverOnly === true ||
(await DiskMangerWorker.excludeDir(
file,
relativeDirectoryName,
@ -156,11 +156,11 @@ export class DiskMangerWorker {
continue;
}
// create preview directory
// create cover directory
const d = (await DiskMangerWorker.scanDirectory(
path.join(relativeDirectoryName, file),
{
previewOnly: true,
coverOnly: true,
}
)) as SubDirectoryDTO;
@ -182,27 +182,27 @@ export class DiskMangerWorker {
: await MetadataLoader.loadPhotoMetadata(fullFilePath),
} as PhotoDTO;
if (!directory.preview) {
directory.preview = Utils.clone(photo);
if (!directory.cover) {
directory.cover = Utils.clone(photo);
directory.preview.directory = {
directory.cover.directory = {
path: directory.path,
name: directory.name,
};
}
// add the preview photo to the list of media, so it will be saved to the DB
// and can be queried to populate previews,
// add the cover photo to the list of media, so it will be saved to the DB
// and can be queried to populate covers,
// otherwise we do not return media list that is only partial
directory.media.push(photo);
if (settings.previewOnly === true) {
if (settings.coverOnly === true) {
break;
}
} else if (VideoProcessing.isVideo(fullFilePath)) {
if (
Config.Media.Video.enabled === false ||
settings.noVideo === true ||
settings.previewOnly === true
settings.coverOnly === true
) {
continue;
}
@ -227,7 +227,7 @@ export class DiskMangerWorker {
if (
!DiskMangerWorker.isEnabledMetaFile(fullFilePath) ||
settings.noMetaFile === true ||
settings.previewOnly === true
settings.coverOnly === true
) {
continue;
}
@ -261,7 +261,7 @@ export class DiskMangerWorker {
}
export interface DirectoryScanSettings {
previewOnly?: boolean;
coverOnly?: boolean;
noMetaFile?: boolean;
noVideo?: boolean;
noPhoto?: boolean;

View File

@ -45,7 +45,6 @@ export class MetadataLoader {
try {
for (const stream of data.streams) {
console.log(stream);
if (stream.width) {
metadata.size.width = stream.width;
metadata.size.height = stream.height;

View File

@ -1,4 +1,4 @@
/**
* This version indicates that the sql/entities/*Entity.ts files got changed and the db needs to be recreated
*/
export const DataStructureVersion = 31;
export const DataStructureVersion = 32;

View File

@ -667,15 +667,15 @@ export class ServerJobConfig {
{indexChangesOnly: true} // set config explicitly, so it is not undefined on the UI
),
new JobScheduleConfig(
DefaultsJobs[DefaultsJobs['Preview Filling']],
DefaultsJobs[DefaultsJobs['Preview Filling']],
DefaultsJobs[DefaultsJobs['Album Cover Filling']],
DefaultsJobs[DefaultsJobs['Album Cover Filling']],
new AfterJobTriggerConfig(DefaultsJobs[DefaultsJobs['Indexing']]),
{}
),
new JobScheduleConfig(
DefaultsJobs[DefaultsJobs['Thumbnail Generation']],
DefaultsJobs[DefaultsJobs['Thumbnail Generation']],
new AfterJobTriggerConfig(DefaultsJobs[DefaultsJobs['Preview Filling']]),
new AfterJobTriggerConfig(DefaultsJobs[DefaultsJobs['Album Cover Filling']]),
{sizes: [240], indexedOnly: true}
),
new JobScheduleConfig(
@ -872,11 +872,11 @@ export class ServerPhotoConfig extends ClientPhotoConfig {
}
@SubConfigClass({softReadonly: true})
export class ServerPreviewConfig {
export class ServerAlbumCoverConfig {
@ConfigProperty({
type: 'object',
tags: {
name: $localize`Preview Filter query`,
name: $localize`Cover Filter query`,
uiResetNeeded: {db: true},
priority: ConfigPriority.advanced,
uiType: 'SearchQuery'
@ -890,11 +890,11 @@ export class ServerPreviewConfig {
@ConfigProperty({
arrayType: SortingMethods,
tags: {
name: $localize`Preview Sorting`,
name: $localize`Cover Sorting`,
uiResetNeeded: {db: true},
priority: ConfigPriority.advanced
},
description: $localize`If multiple preview is available sorts them by these methods and selects the first one.`,
description: $localize`If multiple cover is available sorts them by these methods and selects the first one.`,
})
Sorting: SortingMethods[] = [
SortingMethods.descRating,
@ -1120,18 +1120,18 @@ export class ServerConfig extends ClientConfig {
@ConfigProperty({
tags: {
name: $localize`Preview`,
name: $localize`Album cover`,
uiIcon: 'ionImageOutline',
uiJob: [
{
job: DefaultsJobs[DefaultsJobs['Preview Filling']],
job: DefaultsJobs[DefaultsJobs['Album Cover Filling']],
}, {
job: DefaultsJobs[DefaultsJobs['Preview Reset']],
job: DefaultsJobs[DefaultsJobs['Album Cover Reset']],
hideProgress: true
}]
} as TAGS
})
Preview: ServerPreviewConfig = new ServerPreviewConfig();
AlbumCover: ServerAlbumCoverConfig = new ServerAlbumCoverConfig();
@ConfigProperty({
tags: {

View File

@ -251,15 +251,15 @@ export class ContentWrapper {
}
private static packDirectory(cw: ContentWrapper, dir: DirectoryBaseDTO | SearchResultDTO, isSearchResult = false): void {
if ((dir as DirectoryBaseDTO).preview) {
(dir as DirectoryBaseDTO).preview.directory = {
path: (dir as DirectoryBaseDTO).preview.directory.path,
name: (dir as DirectoryBaseDTO).preview.directory.name,
if ((dir as DirectoryBaseDTO).cover) {
(dir as DirectoryBaseDTO).cover.directory = {
path: (dir as DirectoryBaseDTO).cover.directory.path,
name: (dir as DirectoryBaseDTO).cover.directory.name,
} as DirectoryPathDTO;
// make sure that it is not a same object as one of the photo in the media[]
// as the next foreach would remove the directory
(dir as DirectoryBaseDTO).preview = Utils.clone((dir as DirectoryBaseDTO).preview);
(dir as DirectoryBaseDTO).cover = Utils.clone((dir as DirectoryBaseDTO).cover);
}
if (dir.media) {
@ -284,7 +284,7 @@ export class ContentWrapper {
}
}
delete (dir as DirectoryBaseDTO).validPreview; // should not go to the client side;
delete (dir as DirectoryBaseDTO).validCover; // should not go to the client side;
}
private static deMapify(cw: ContentWrapper, media: FileDTO, isSearchResult: boolean): void {

View File

@ -1,6 +1,6 @@
import { MediaDTO, MediaDTOUtils } from './MediaDTO';
import { FileDTO } from './FileDTO';
import { PhotoDTO, PreviewPhotoDTO } from './PhotoDTO';
import { PhotoDTO, CoverPhotoDTO } from './PhotoDTO';
import { Utils } from '../Utils';
export interface DirectoryPathDTO {
@ -24,8 +24,8 @@ export interface DirectoryBaseDTO<S extends FileDTO = MediaDTO>
directories?: DirectoryBaseDTO<S>[];
media?: S[];
metaFile?: FileDTO[];
preview?: PreviewPhotoDTO;
validPreview?: boolean; // does not go to the client side
cover?: CoverPhotoDTO;
validCover?: boolean; // does not go to the client side
}
export interface ParentDirectoryDTO<S extends FileDTO = MediaDTO>
@ -53,8 +53,8 @@ export interface SubDirectoryDTO<S extends FileDTO = MediaDTO>
isPartial?: boolean;
parent: ParentDirectoryDTO<S>;
mediaCount: number;
preview: PreviewPhotoDTO;
validPreview?: boolean; // does not go to the client side
cover: CoverPhotoDTO;
validCover?: boolean; // does not go to the client side
}
export const DirectoryDTOUtils = {
@ -78,15 +78,15 @@ export const DirectoryDTOUtils = {
},
removeReferences: (dir: DirectoryBaseDTO): DirectoryBaseDTO => {
if (dir.preview) {
dir.preview.directory = {
path: dir.preview.directory.path,
name: dir.preview.directory.name,
if (dir.cover) {
dir.cover.directory = {
path: dir.cover.directory.path,
name: dir.cover.directory.name,
} as DirectoryPathDTO;
// make sure that it is not a same object as one of the photo in the media[]
// as the next foreach would remove the directory
dir.preview = Utils.clone(dir.preview);
dir.cover = Utils.clone(dir.cover);
}
if (dir.media) {
@ -107,7 +107,7 @@ export const DirectoryDTOUtils = {
});
}
delete dir.validPreview; // should not go to the client side;
delete dir.validCover; // should not go to the client side;
return dir;
},

View File

@ -1,12 +1,12 @@
import { DirectoryPathDTO } from './DirectoryDTO';
import { MediaDimension, MediaDTO, MediaMetadata } from './MediaDTO';
export interface PreviewPhotoDTO extends MediaDTO {
export interface CoverPhotoDTO extends MediaDTO {
name: string;
directory: DirectoryPathDTO;
}
export interface PhotoDTO extends PreviewPhotoDTO, MediaDTO {
export interface PhotoDTO extends CoverPhotoDTO, MediaDTO {
id: number;
name: string;
directory: DirectoryPathDTO;

View File

@ -1,9 +1,9 @@
import { PreviewPhotoDTO } from '../PhotoDTO';
import { CoverPhotoDTO } from '../PhotoDTO';
export interface AlbumBaseDTO {
id: number;
name: string;
preview?: PreviewPhotoDTO;
cover?: CoverPhotoDTO;
count: number;
locked: boolean;
}

View File

@ -1,11 +1,11 @@
import { AlbumBaseDTO } from './AlbumBaseDTO';
import { PreviewPhotoDTO } from '../PhotoDTO';
import { CoverPhotoDTO } from '../PhotoDTO';
import { SearchQueryDTO } from '../SearchQueryDTO';
export interface SavedSearchDTO extends AlbumBaseDTO {
id: number;
name: string;
preview?: PreviewPhotoDTO;
cover?: CoverPhotoDTO;
count: number;
locked: boolean;

View File

@ -9,8 +9,8 @@ export enum DefaultsJobs {
'Photo Converting' = 4,
'Thumbnail Generation' = 5,
'Temp Folder Cleaning' = 6,
'Preview Filling' = 7,
'Preview Reset' = 8,
'Album Cover Filling' = 7,
'Album Cover Reset' = 8,
'GPX Compression' = 9,
'Album Reset' = 10,
'Delete Compressed GPX' = 11,

View File

@ -59,10 +59,10 @@ export class BackendtextService {
return $localize`Video converting`;
case DefaultsJobs['Temp Folder Cleaning']:
return $localize`Temp folder cleaning`;
case DefaultsJobs['Preview Filling']:
return $localize`Preview filling`;
case DefaultsJobs['Preview Reset']:
return $localize`Preview reset`;
case DefaultsJobs['Album Cover Filling']:
return $localize`Album Cover filling`;
case DefaultsJobs['Album Cover Reset']:
return $localize`Album Cover reset`;
case DefaultsJobs['GPX Compression']:
return $localize`GPX compression`;
default:

View File

@ -54,9 +54,9 @@ export class AlbumComponent implements OnInit, OnDestroy {
}
ngOnInit(): void {
if (this.album.preview) {
if (this.album.cover) {
this.thumbnail = this.thumbnailService.getThumbnail(
new Media(this.album.preview, this.size, this.size)
new Media(this.album.cover, this.size, this.size)
);
}
}

View File

@ -6,7 +6,7 @@ import {Utils} from '../../../../../../common/Utils';
import {Media} from '../../Media';
import {Thumbnail, ThumbnailManagerService,} from '../../thumbnailManager.service';
import {QueryService} from '../../../../model/query.service';
import {PreviewPhotoDTO} from '../../../../../../common/entities/PhotoDTO';
import {CoverPhotoDTO} from '../../../../../../common/entities/PhotoDTO';
import {Config} from '../../../../../../common/config/public/Config';
@Component({
@ -27,8 +27,8 @@ export class GalleryDirectoryComponent implements OnInit, OnDestroy {
) {
}
public get SamplePhoto(): PreviewPhotoDTO {
return this.directory.preview;
public get SamplePhoto(): CoverPhotoDTO {
return this.directory.cover;
}
getSanitizedThUrl(): SafeStyle {
@ -58,7 +58,7 @@ export class GalleryDirectoryComponent implements OnInit, OnDestroy {
}
ngOnInit(): void {
if (this.directory.preview) {
if (this.directory.cover) {
this.thumbnail = this.thumbnailService.getThumbnail(
new Media(this.SamplePhoto, this.size, this.size)
);

View File

@ -257,7 +257,7 @@ export class GalleryGridComponent
}
// TODO: This is deprecated,
// we do not post update galleries anymore since the preview member in the DriectoryDTO
// we do not post update galleries anymore since the cover member in the DriectoryDTO
private mergeNewPhotos(): void {
// merge new data with old one
let lastSameIndex = 0;

View File

@ -34,7 +34,7 @@
{{Name}} <span i18n>config is not supported with these settings.</span>
</div>
<div class="row mt-2">
<div class="col" *ngIf="states.tags?.uiJob && !states.tags?.uiJob[0].description">
<div class="col-auto" *ngIf="states.tags?.uiJob && !states.tags?.uiJob[0].description">
<ng-container
*ngTemplateOutlet="JobTemplate; context:{ uiJob: states.tags?.uiJob }"
></ng-container>

View File

@ -15,7 +15,7 @@ import {
PhotoDTO,
PhotoMetadata,
PositionMetaData,
PreviewPhotoDTO
CoverPhotoDTO
} from '../src/common/entities/PhotoDTO';
import {DirectoryBaseDTO, DirectoryPathDTO} from '../src/common/entities/DirectoryDTO';
import {FileDTO} from '../src/common/entities/FileDTO';
@ -274,8 +274,8 @@ export class TestHelper {
mediaCount: 0,
directories: [],
metaFile: [],
preview: null,
validPreview: false,
cover: null,
validCover: false,
media: [],
lastModified: Date.now(),
lastScanned: null,
@ -380,21 +380,21 @@ export class TestHelper {
}
dir.media.push(p);
TestHelper.updatePreview(dir);
TestHelper.updateCover(dir);
return p;
}
static updatePreview(dir: DirectoryBaseDTO): void {
static updateCover(dir: DirectoryBaseDTO): void {
if (dir.media.length > 0) {
dir.preview = dir.media.sort((a, b): number => b.metadata.creationDate - a.metadata.creationDate)[0];
dir.cover = dir.media.sort((a, b): number => b.metadata.creationDate - a.metadata.creationDate)[0];
} else {
const filtered = dir.directories.filter((d): PreviewPhotoDTO => d.preview).map((d): PreviewPhotoDTO => d.preview);
const filtered = dir.directories.filter((d): CoverPhotoDTO => d.cover).map((d): CoverPhotoDTO => d.cover);
if (filtered.length > 0) {
dir.preview = filtered.sort((a, b): number => b.metadata.creationDate - a.metadata.creationDate)[0];
dir.cover = filtered.sort((a, b): number => b.metadata.creationDate - a.metadata.creationDate)[0];
}
}
if (dir.parent) {
TestHelper.updatePreview(dir.parent);
TestHelper.updateCover(dir.parent);
}
}

View File

@ -37,16 +37,16 @@ describe('AlbumManager', (sqlHelper: DBTestHelper) => {
};
const toAlbumPreview = (m: MediaDTO): MediaDTO => {
const toAlbumCover = (m: MediaDTO): MediaDTO => {
// generated dirs for test contain everything, not like return values from the server.
const tmpDir: ParentDirectoryDTO = m.directory as ParentDirectoryDTO;
const tmpM = tmpDir.media;
const tmpD = tmpDir.directories;
const tmpP = tmpDir.preview;
const tmpP = tmpDir.cover;
const tmpMT = tmpDir.metaFile;
delete tmpDir.directories;
delete tmpDir.media;
delete tmpDir.preview;
delete tmpDir.cover;
delete tmpDir.metaFile;
const ret = Utils.clone(m);
delete ret.id;
@ -54,7 +54,7 @@ describe('AlbumManager', (sqlHelper: DBTestHelper) => {
delete ret.metadata;
tmpDir.directories = tmpD;
tmpDir.media = tmpM;
tmpDir.preview = tmpP;
tmpDir.cover = tmpP;
tmpDir.metaFile = tmpMT;
return ret;
};
@ -152,7 +152,7 @@ describe('AlbumManager', (sqlHelper: DBTestHelper) => {
searchQuery: query,
locked: false,
count: 1,
preview: toAlbumPreview(sqlHelper.testGalleyEntities.p)
cover: toAlbumCover(sqlHelper.testGalleyEntities.p)
} as SavedSearchDTO]));

View File

@ -71,13 +71,13 @@ describe('IndexingManager', (sqlHelper: DBTestHelper) => {
afterEach(async () => {
Config.loadSync();
Config.Preview.Sorting = [SortingMethods.descRating];
Config.AlbumCover.Sorting = [SortingMethods.descRating];
await sqlHelper.clearDB();
});
const setPartial = (dir: DirectoryBaseDTO) => {
if (!dir.preview && dir.media && dir.media.length > 0) {
dir.preview = dir.media[0];
if (!dir.cover && dir.media && dir.media.length > 0) {
dir.cover = dir.media[0];
}
dir.isPartial = true;
delete dir.directories;
@ -94,11 +94,11 @@ describe('IndexingManager', (sqlHelper: DBTestHelper) => {
const indexifyReturn = (dir: DirectoryBaseDTO): DirectoryBaseDTO => {
const d = Utils.clone(dir);
delete d.preview;
delete d.cover;
if (d.directories) {
for (const subD of d.directories) {
if (subD.preview) {
delete subD.preview.metadata;
if (subD.cover) {
delete subD.cover.metadata;
}
}
}
@ -111,8 +111,8 @@ describe('IndexingManager', (sqlHelper: DBTestHelper) => {
dir.media.forEach((media: MediaDTO) => {
delete media.id;
});
if (dir.preview) {
delete dir.preview.id;
if (dir.cover) {
delete dir.cover.id;
}
if (dir.metaFile) {
if (dir.metaFile.length === 0) {
@ -288,7 +288,7 @@ describe('IndexingManager', (sqlHelper: DBTestHelper) => {
});
it('should select preview', async () => {
it('should select cover', async () => {
const selectDirectory = async (gmTest: GalleryManagerTest, dir: DirectoryBaseDTO): Promise<ParentDirectoryDTO> => {
const conn = await SQLConnection.getConnection();
const selected = await gmTest.getParentDirFromId(conn,
@ -357,8 +357,8 @@ describe('IndexingManager', (sqlHelper: DBTestHelper) => {
sp1.metadata.rating = 5;
const sp2 = TestHelper.getRandomizedPhotoEntry(subDir, 'subPhoto2', 0);
sp2.metadata.rating = 3;
subDir.preview = sp1;
Config.Preview.Sorting = [SortingMethods.descRating];
subDir.cover = sp1;
Config.AlbumCover.Sorting = [SortingMethods.descRating];
DirectoryDTOUtils.removeReferences(subDir);
await im.saveToDB(Utils.clone(subDir) as ParentDirectoryDTO);
@ -395,8 +395,8 @@ describe('IndexingManager', (sqlHelper: DBTestHelper) => {
sp1.metadata.rating = 5;
const sp2 = TestHelper.getRandomizedPhotoEntry(subDir, 'subPhoto2', 0);
sp2.metadata.rating = 3;
subDir.preview = sp1;
Config.Preview.Sorting = [SortingMethods.descRating];
subDir.cover = sp1;
Config.AlbumCover.Sorting = [SortingMethods.descRating];
DirectoryDTOUtils.removeReferences(subDir);
@ -432,8 +432,8 @@ describe('IndexingManager', (sqlHelper: DBTestHelper) => {
sp1.metadata.rating = 5;
const sp2 = TestHelper.getRandomizedPhotoEntry(subDir, 'subPhoto2', 0);
sp2.metadata.rating = 3;
subDir.preview = sp1;
Config.Preview.Sorting = [SortingMethods.descRating];
subDir.cover = sp1;
Config.AlbumCover.Sorting = [SortingMethods.descRating];
DirectoryDTOUtils.removeReferences(parent);
await im.saveToDB(Utils.clone(parent) as ParentDirectoryDTO);
@ -562,8 +562,8 @@ describe('IndexingManager', (sqlHelper: DBTestHelper) => {
sp1.metadata.rating = 5;
const sp2 = TestHelper.getRandomizedPhotoEntry(subDir, 'subPhoto2', 1);
sp2.metadata.rating = 3;
subDir.preview = sp1;
Config.Preview.Sorting = [SortingMethods.descRating];
subDir.cover = sp1;
Config.AlbumCover.Sorting = [SortingMethods.descRating];
DirectoryDTOUtils.removeReferences(parent);
const s1 = im.queueForSave(Utils.clone(parent) as ParentDirectoryDTO);
@ -578,7 +578,7 @@ describe('IndexingManager', (sqlHelper: DBTestHelper) => {
DirectoryDTOUtils.removeReferences(selected);
removeIds(selected);
setPartial(subDir);
parent.directories.forEach(d => delete (d.preview.metadata as any).faces);
parent.directories.forEach(d => delete (d.cover.metadata as any).faces);
delete sp1.metadata.faces;
delete sp2.metadata.faces;
expect(Utils.clone(Utils.removeNullOrEmptyObj(selected)))
@ -709,8 +709,8 @@ describe('IndexingManager', (sqlHelper: DBTestHelper) => {
await im.saveToDB(dir);
const albums = await am.getAlbums();
expect(albums[0].preview).to.be.an('object');
delete albums[0].preview;
expect(albums[0].cover).to.be.an('object');
delete albums[0].cover;
expect(albums).to.be.deep.equal([
{
id: 1,

View File

@ -10,7 +10,7 @@ import {Connection} from 'typeorm';
import {PhotoDTO} from '../../../../../src/common/entities/PhotoDTO';
import {VideoDTO} from '../../../../../src/common/entities/VideoDTO';
import {FileDTO} from '../../../../../src/common/entities/FileDTO';
import {PreviewManager} from '../../../../../src/backend/model/database/PreviewManager';
import {CoverManager} from '../../../../../src/backend/model/database/CoverManager';
import {Config} from '../../../../../src/common/config/private/Config';
import {SortingMethods} from '../../../../../src/common/entities/SortingMethods';
import {Utils} from '../../../../../src/common/Utils';
@ -59,7 +59,7 @@ class GalleryManagerTest extends GalleryManager {
}
}
describe('PreviewManager', (sqlHelper: DBTestHelper) => {
describe('CoverManager', (sqlHelper: DBTestHelper) => {
describe = tmpDescribe;
/**
* dir
@ -134,12 +134,12 @@ describe('PreviewManager', (sqlHelper: DBTestHelper) => {
const tmpDir: DirectoryBaseDTO = m.directory as DirectoryBaseDTO;
const tmpM = tmpDir.media;
const tmpD = tmpDir.directories;
const tmpP = tmpDir.preview;
const tmpP = tmpDir.cover;
const tmpMT = tmpDir.metaFile;
delete tmpDir.directories;
delete tmpDir.media;
delete tmpDir.preview;
delete tmpDir.validPreview;
delete tmpDir.cover;
delete tmpDir.validCover;
delete tmpDir.metaFile;
const ret = Utils.clone(m);
delete (ret.directory as DirectoryBaseDTO).id;
@ -149,7 +149,7 @@ describe('PreviewManager', (sqlHelper: DBTestHelper) => {
delete (ret as PhotoDTO).metadata;
tmpDir.directories = tmpD;
tmpDir.media = tmpM;
tmpDir.preview = tmpP;
tmpDir.cover = tmpP;
tmpDir.metaFile = tmpMT;
return ret;
};
@ -160,83 +160,83 @@ describe('PreviewManager', (sqlHelper: DBTestHelper) => {
});
afterEach(() => {
Config.Preview.SearchQuery = null;
Config.Preview.Sorting = [SortingMethods.descRating, SortingMethods.descDate];
Config.AlbumCover.SearchQuery = null;
Config.AlbumCover.Sorting = [SortingMethods.descRating, SortingMethods.descDate];
});
it('should list directories without preview', async () => {
const pm = new PreviewManager();
it('should list directories without cover', async () => {
const pm = new CoverManager();
const partialDir = (d: DirectoryBaseDTO) => {
return {id: d.id, name: d.name, path: d.path};
};
expect(await pm.getPartialDirsWithoutPreviews()).to.deep.equalInAnyOrder([partialDir(dir)]);
expect(await pm.getPartialDirsWithoutCovers()).to.deep.equalInAnyOrder([partialDir(dir)]);
const conn = await SQLConnection.getConnection();
await conn.createQueryBuilder()
.update(DirectoryEntity).set({validPreview: false}).execute();
.update(DirectoryEntity).set({validCover: false}).execute();
expect(await pm.getPartialDirsWithoutPreviews()).to.deep.equalInAnyOrder([dir, subDir, subDir2].map(d => partialDir(d)));
expect(await pm.getPartialDirsWithoutCovers()).to.deep.equalInAnyOrder([dir, subDir, subDir2].map(d => partialDir(d)));
});
it('should sort directory preview', async () => {
const pm = new PreviewManager();
Config.Preview.Sorting = [SortingMethods.descRating, SortingMethods.descDate];
expect(Utils.clone(await pm.setAndGetPreviewForDirectory(subDir))).to.deep.equalInAnyOrder(previewifyMedia(p2));
Config.Preview.Sorting = [SortingMethods.descDate];
expect(Utils.clone(await pm.setAndGetPreviewForDirectory(subDir))).to.deep.equalInAnyOrder(previewifyMedia(pFaceLess));
Config.Preview.Sorting = [SortingMethods.descRating];
expect(Utils.clone(await pm.setAndGetPreviewForDirectory(dir))).to.deep.equalInAnyOrder(previewifyMedia(p4));
Config.Preview.Sorting = [SortingMethods.descName];
expect(Utils.clone(await pm.setAndGetPreviewForDirectory(dir))).to.deep.equalInAnyOrder(previewifyMedia(v));
it('should sort directory cover', async () => {
const pm = new CoverManager();
Config.AlbumCover.Sorting = [SortingMethods.descRating, SortingMethods.descDate];
expect(Utils.clone(await pm.setAndGetCoverForDirectory(subDir))).to.deep.equalInAnyOrder(previewifyMedia(p2));
Config.AlbumCover.Sorting = [SortingMethods.descDate];
expect(Utils.clone(await pm.setAndGetCoverForDirectory(subDir))).to.deep.equalInAnyOrder(previewifyMedia(pFaceLess));
Config.AlbumCover.Sorting = [SortingMethods.descRating];
expect(Utils.clone(await pm.setAndGetCoverForDirectory(dir))).to.deep.equalInAnyOrder(previewifyMedia(p4));
Config.AlbumCover.Sorting = [SortingMethods.descName];
expect(Utils.clone(await pm.setAndGetCoverForDirectory(dir))).to.deep.equalInAnyOrder(previewifyMedia(v));
});
it('should get preview for directory', async () => {
const pm = new PreviewManager();
it('should get cover for directory', async () => {
const pm = new CoverManager();
Config.Preview.SearchQuery = {type: SearchQueryTypes.any_text, text: 'Boba'} as TextSearch;
expect(Utils.clone(await pm.setAndGetPreviewForDirectory(subDir))).to.deep.equalInAnyOrder(previewifyMedia(p));
Config.Preview.SearchQuery = {type: SearchQueryTypes.any_text, text: 'Derem'} as TextSearch;
expect(Utils.clone(await pm.setAndGetPreviewForDirectory(subDir))).to.deep.equalInAnyOrder(previewifyMedia(p2));
expect(Utils.clone(await pm.setAndGetPreviewForDirectory(dir))).to.deep.equalInAnyOrder(previewifyMedia(p2));
expect(Utils.clone(await pm.setAndGetPreviewForDirectory(subDir2))).to.deep.equalInAnyOrder(previewifyMedia(p4));
Config.AlbumCover.SearchQuery = {type: SearchQueryTypes.any_text, text: 'Boba'} as TextSearch;
expect(Utils.clone(await pm.setAndGetCoverForDirectory(subDir))).to.deep.equalInAnyOrder(previewifyMedia(p));
Config.AlbumCover.SearchQuery = {type: SearchQueryTypes.any_text, text: 'Derem'} as TextSearch;
expect(Utils.clone(await pm.setAndGetCoverForDirectory(subDir))).to.deep.equalInAnyOrder(previewifyMedia(p2));
expect(Utils.clone(await pm.setAndGetCoverForDirectory(dir))).to.deep.equalInAnyOrder(previewifyMedia(p2));
expect(Utils.clone(await pm.setAndGetCoverForDirectory(subDir2))).to.deep.equalInAnyOrder(previewifyMedia(p4));
});
it('should get preview for saved search', async () => {
const pm = new PreviewManager();
Config.Preview.SearchQuery = null;
expect(Utils.clone(await pm.getAlbumPreview({
it('should get cover for saved search', async () => {
const pm = new CoverManager();
Config.AlbumCover.SearchQuery = null;
expect(Utils.clone(await pm.getAlbumCover({
searchQuery: {
type: SearchQueryTypes.any_text,
text: 'sw'
} as TextSearch
}))).to.deep.equalInAnyOrder(previewifyMedia(p4));
Config.Preview.SearchQuery = {type: SearchQueryTypes.any_text, text: 'Boba'} as TextSearch;
expect(Utils.clone(await pm.getAlbumPreview({
Config.AlbumCover.SearchQuery = {type: SearchQueryTypes.any_text, text: 'Boba'} as TextSearch;
expect(Utils.clone(await pm.getAlbumCover({
searchQuery: {
type: SearchQueryTypes.any_text,
text: 'sw'
} as TextSearch
}))).to.deep.equalInAnyOrder(previewifyMedia(p));
Config.Preview.SearchQuery = {type: SearchQueryTypes.any_text, text: 'Derem'} as TextSearch;
expect(Utils.clone(await pm.getAlbumPreview({
Config.AlbumCover.SearchQuery = {type: SearchQueryTypes.any_text, text: 'Derem'} as TextSearch;
expect(Utils.clone(await pm.getAlbumCover({
searchQuery: {
type: SearchQueryTypes.any_text,
text: 'sw'
} as TextSearch
}))).to.deep.equalInAnyOrder(previewifyMedia(p2));
// Having a preview search query that does not return valid result
Config.Preview.SearchQuery = {type: SearchQueryTypes.any_text, text: 'wont find it'} as TextSearch;
expect(Utils.clone(await pm.getAlbumPreview({
Config.AlbumCover.SearchQuery = {type: SearchQueryTypes.any_text, text: 'wont find it'} as TextSearch;
expect(Utils.clone(await pm.getAlbumCover({
searchQuery: {
type: SearchQueryTypes.any_text,
text: 'Derem'
} as TextSearch
}))).to.deep.equalInAnyOrder(previewifyMedia(p2));
// having a saved search that does not have any image
Config.Preview.SearchQuery = {type: SearchQueryTypes.any_text, text: 'Derem'} as TextSearch;
expect(Utils.clone(await pm.getAlbumPreview({
Config.AlbumCover.SearchQuery = {type: SearchQueryTypes.any_text, text: 'Derem'} as TextSearch;
expect(Utils.clone(await pm.getAlbumCover({
searchQuery: {
type: SearchQueryTypes.any_text,
text: 'wont find it'
@ -244,9 +244,9 @@ describe('PreviewManager', (sqlHelper: DBTestHelper) => {
}))).to.deep.equal(null);
});
it('should invalidate and update preview', async () => {
it('should invalidate and update cover', async () => {
const gm = new GalleryManagerTest();
const pm = new PreviewManager();
const pm = new CoverManager();
const conn = await SQLConnection.getConnection();
const selectDir = async () => {
@ -254,7 +254,7 @@ describe('PreviewManager', (sqlHelper: DBTestHelper) => {
where: {id: subDir.id},
join: {
alias: 'dir',
leftJoinAndSelect: {preview: 'dir.preview'}
leftJoinAndSelect: {cover: 'dir.cover'}
}
});
};
@ -262,27 +262,27 @@ describe('PreviewManager', (sqlHelper: DBTestHelper) => {
let subdir = await selectDir();
expect(subdir.validPreview).to.equal(true);
expect(subdir.preview.id).to.equal(p2.id);
expect(subdir.validCover).to.equal(true);
expect(subdir.cover.id).to.equal(p2.id);
// new version should invalidate
await pm.onNewDataVersion(subDir as ParentDirectoryDTO);
subdir = await selectDir();
expect(subdir.validPreview).to.equal(false);
// during invalidation, we do not remove the previous preview (it's good to show at least some photo)
expect(subdir.preview.id).to.equal(p2.id);
expect(subdir.validCover).to.equal(false);
// during invalidation, we do not remove the previous cover (it's good to show at least some photo)
expect(subdir.cover.id).to.equal(p2.id);
await conn.createQueryBuilder()
.update(DirectoryEntity)
.set({validPreview: false, preview: null}).execute();
expect((await selectDir()).preview).to.equal(null);
.set({validCover: false, cover: null}).execute();
expect((await selectDir()).cover).to.equal(null);
const res = await gm.getParentDirFromId(conn,
(await gm.getDirIdAndTime(conn, dir.name, dir.path)).id);
subdir = await selectDir();
expect(subdir.validPreview).to.equal(true);
expect(subdir.preview.id).to.equal(p2.id);
expect(subdir.validCover).to.equal(true);
expect(subdir.cover.id).to.equal(p2.id);
});

View File

@ -218,12 +218,12 @@ describe('SearchManager', (sqlHelper: DBTestHelper) => {
const tmpDir: DirectoryBaseDTO = m.directory as DirectoryBaseDTO;
const tmpM = tmpDir.media;
const tmpD = tmpDir.directories;
const tmpP = tmpDir.preview;
const tmpP = tmpDir.cover;
const tmpMT = tmpDir.metaFile;
delete tmpDir.directories;
delete tmpDir.media;
delete tmpDir.preview;
delete tmpDir.validPreview;
delete tmpDir.cover;
delete tmpDir.validCover;
delete tmpDir.metaFile;
const ret = Utils.clone(m);
delete (ret.directory as DirectoryBaseDTO).lastScanned;
@ -235,7 +235,7 @@ describe('SearchManager', (sqlHelper: DBTestHelper) => {
}
tmpDir.directories = tmpD;
tmpDir.media = tmpM;
tmpDir.preview = tmpP;
tmpDir.cover = tmpP;
tmpDir.metaFile = tmpMT;
return ret;
};
@ -243,7 +243,7 @@ describe('SearchManager', (sqlHelper: DBTestHelper) => {
const searchifyDir = (d: DirectoryBaseDTO): DirectoryBaseDTO => {
const tmpM = d.media;
const tmpD = d.directories;
const tmpP = d.preview;
const tmpP = d.cover;
const tmpMT = d.metaFile;
delete d.directories;
delete d.media;
@ -251,7 +251,7 @@ describe('SearchManager', (sqlHelper: DBTestHelper) => {
const ret = Utils.clone(d);
d.directories = tmpD;
d.media = tmpM;
d.preview = tmpP;
d.cover = tmpP;
d.metaFile = tmpMT;
ret.isPartial = true;
return ret;
@ -1433,8 +1433,8 @@ describe('SearchManager', (sqlHelper: DBTestHelper) => {
const sm = new SearchManager();
const cloned = Utils.clone(searchifyDir(subDir));
cloned.validPreview = true;
cloned.preview = {
cloned.validCover = true;
cloned.cover = {
directory: {
name: subDir.name,
path: subDir.path