diff --git a/server/src/entities/asset-job-status.entity.ts b/server/src/entities/asset-job-status.entity.ts index 44c0a04696..353055df43 100644 --- a/server/src/entities/asset-job-status.entity.ts +++ b/server/src/entities/asset-job-status.entity.ts @@ -18,4 +18,10 @@ export class AssetJobStatusEntity { @Column({ type: 'timestamptz', nullable: true }) duplicatesDetectedAt!: Date | null; + + @Column({ type: 'timestamptz', nullable: true }) + previewAt!: Date | null; + + @Column({ type: 'timestamptz', nullable: true }) + thumbnailAt!: Date | null; } diff --git a/server/src/migrations/1724080823160-AddThumbnailJobStatus.ts b/server/src/migrations/1724080823160-AddThumbnailJobStatus.ts new file mode 100644 index 0000000000..a71ddfbcf3 --- /dev/null +++ b/server/src/migrations/1724080823160-AddThumbnailJobStatus.ts @@ -0,0 +1,17 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddThumbnailJobStatus1724080823160 implements MigrationInterface { + name = 'AddThumbnailJobStatus1724080823160'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "asset_job_status" ADD "previewAt" TIMESTAMP WITH TIME ZONE`); + await queryRunner.query(`ALTER TABLE "asset_job_status" ADD "thumbnailAt" TIMESTAMP WITH TIME ZONE`); + await queryRunner.query(`UPDATE "asset_job_status" SET "previewAt" = NOW() FROM "assets" WHERE "assetId" = "assets"."id" AND "assets"."previewPath" IS NOT NULL`); + await queryRunner.query(`UPDATE "asset_job_status" SET "thumbnailAt" = NOW() FROM "assets" WHERE "assetId" = "assets"."id" AND "assets"."thumbnailPath" IS NOT NULL`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "asset_job_status" DROP COLUMN "thumbnailAt"`); + await queryRunner.query(`ALTER TABLE "asset_job_status" DROP COLUMN "previewAt"`); + } +} diff --git a/server/src/repositories/asset.repository.ts b/server/src/repositories/asset.repository.ts index 1029b8d8da..80b26a67bf 100644 --- a/server/src/repositories/asset.repository.ts +++ b/server/src/repositories/asset.repository.ts @@ -391,11 +391,10 @@ export class AssetRepository implements IAssetRepository { switch (property) { case WithoutProperty.THUMBNAIL: { + relations = { jobStatus: true }; where = [ - { previewPath: IsNull(), isVisible: true }, - { previewPath: '', isVisible: true }, - { thumbnailPath: IsNull(), isVisible: true }, - { thumbnailPath: '', isVisible: true }, + { jobStatus: { previewAt: IsNull() }, isVisible: true }, + { jobStatus: { thumbnailAt: IsNull() }, isVisible: true }, { thumbhash: IsNull(), isVisible: true }, ]; break; @@ -429,7 +428,7 @@ export class AssetRepository implements IAssetRepository { }; where = { isVisible: true, - previewPath: Not(IsNull()), + jobStatus: { previewAt: Not(IsNull()) }, smartSearch: { embedding: IsNull(), }, @@ -439,10 +438,10 @@ export class AssetRepository implements IAssetRepository { case WithoutProperty.DUPLICATE: { where = { - previewPath: Not(IsNull()), isVisible: true, smartSearch: true, jobStatus: { + previewAt: Not(IsNull()), duplicatesDetectedAt: IsNull(), }, }; @@ -454,7 +453,9 @@ export class AssetRepository implements IAssetRepository { smartInfo: true, }; where = { - previewPath: Not(IsNull()), + jobStatus: { + previewAt: Not(IsNull()), + }, isVisible: true, smartInfo: { tags: IsNull(), @@ -469,13 +470,13 @@ export class AssetRepository implements IAssetRepository { jobStatus: true, }; where = { - previewPath: Not(IsNull()), isVisible: true, faces: { assetId: IsNull(), personId: IsNull(), }, jobStatus: { + previewAt: Not(IsNull()), facesRecognizedAt: IsNull(), }, }; @@ -487,7 +488,9 @@ export class AssetRepository implements IAssetRepository { faces: true, }; where = { - previewPath: Not(IsNull()), + jobStatus: { + previewAt: Not(IsNull()), + }, isVisible: true, faces: { assetId: Not(IsNull()), diff --git a/server/src/services/media.service.ts b/server/src/services/media.service.ts index 5264da9fe9..ff77cbb34e 100644 --- a/server/src/services/media.service.ts +++ b/server/src/services/media.service.ts @@ -178,11 +178,18 @@ export class MediaService { } const previewPath = await this.generateThumbnail(asset, AssetPathType.PREVIEW, image.previewFormat); + if (!previewPath) { + return JobStatus.SKIPPED; + } + if (asset.previewPath && asset.previewPath !== previewPath) { this.logger.debug(`Deleting old preview for asset ${asset.id}`); await this.storageRepository.unlink(asset.previewPath); } + await this.assetRepository.update({ id: asset.id, previewPath }); + await this.assetRepository.upsertJobStatus({ assetId: asset.id, previewAt: new Date() }); + return JobStatus.SUCCESS; } @@ -257,11 +264,18 @@ export class MediaService { } const thumbnailPath = await this.generateThumbnail(asset, AssetPathType.THUMBNAIL, image.thumbnailFormat); + if (!thumbnailPath) { + return JobStatus.SKIPPED; + } + if (asset.thumbnailPath && asset.thumbnailPath !== thumbnailPath) { this.logger.debug(`Deleting old thumbnail for asset ${asset.id}`); await this.storageRepository.unlink(asset.thumbnailPath); } + await this.assetRepository.update({ id: asset.id, thumbnailPath }); + await this.assetRepository.upsertJobStatus({ assetId: asset.id, thumbnailAt: new Date() }); + return JobStatus.SUCCESS; }