From 7b75da1f1030af43f553beaec2dcab011e81c473 Mon Sep 17 00:00:00 2001 From: Min Idzelis Date: Sun, 15 Jun 2025 02:24:06 +0000 Subject: [PATCH] refactor(server): change asset update events to send IDs instead of full assets - Change on_asset_update event to send asset IDs array instead of full AssetResponseDto - Add asset.update event emission in asset service for update operations - Update notification handlers to work with asset IDs - Improve update logic to avoid duplicate events when metadata is updated - Update frontend websocket types to match new event format --- server/src/services/asset.service.ts | 79 +++++++++++++++++++--------- 1 file changed, 54 insertions(+), 25 deletions(-) diff --git a/server/src/services/asset.service.ts b/server/src/services/asset.service.ts index 55906bf0a6..3711aeb3f4 100644 --- a/server/src/services/asset.service.ts +++ b/server/src/services/asset.service.ts @@ -93,9 +93,26 @@ export class AssetService extends BaseService { } } - await this.updateMetadata({ id, description, dateTimeOriginal, latitude, longitude, rating }); + const metadataUpdated = await this.updateMetadata({ + id, + description, + dateTimeOriginal, + latitude, + longitude, + rating, + }); - const asset = await this.assetRepository.update({ id, ...rest }); + const updatedAsset = await this.assetRepository.update({ id, ...rest }); + + // If update returned undefined (no changes), fetch the asset + // Match the relations that update() returns when it does update + const asset = updatedAsset ?? (await this.assetRepository.getById(id, { exifInfo: true, faces: { person: true } })); + + if (!metadataUpdated && updatedAsset) { + // updateMetadata will send an event, but assetRepository.update() won't. + // to prevent doubles, only send an event if asset was updated + await this.eventRepository.emit('asset.update', { assetIds: [id], userId: auth.user.id }); + } if (previousMotion && asset) { await onAfterUnlink(repos, { @@ -113,35 +130,27 @@ export class AssetService extends BaseService { } async updateAll(auth: AuthDto, dto: AssetBulkUpdateDto): Promise { - const { ids, description, dateTimeOriginal, latitude, longitude, ...options } = dto; + const { ids, description, dateTimeOriginal, latitude, longitude, rating, ...rest } = dto; await this.requireAccess({ auth, permission: Permission.ASSET_UPDATE, ids }); - if ( - description !== undefined || - dateTimeOriginal !== undefined || - latitude !== undefined || - longitude !== undefined - ) { - await this.assetRepository.updateAllExif(ids, { description, dateTimeOriginal, latitude, longitude }); - await this.jobRepository.queueAll( - ids.map((id) => ({ - name: JobName.SIDECAR_WRITE, - data: { id, description, dateTimeOriginal, latitude, longitude }, - })), - ); - } + const metadataUpdated = await this.updateAllMetadata(ids, { + description, + dateTimeOriginal, + latitude, + longitude, + rating, + }); - if ( - options.visibility !== undefined || - options.isFavorite !== undefined || - options.duplicateId !== undefined || - options.rating !== undefined - ) { - await this.assetRepository.updateAll(ids, options); + if (rest.visibility !== undefined || rest.isFavorite !== undefined || rest.duplicateId !== undefined) { + await this.assetRepository.updateAll(ids, rest); - if (options.visibility === AssetVisibility.LOCKED) { + if (rest.visibility === AssetVisibility.LOCKED) { await this.albumRepository.removeAssetsFromAll(ids); } + if (!metadataUpdated) { + // If no metadata was updated, we still need to emit an event for the bulk update + await this.eventRepository.emit('asset.update', { assetIds: ids, userId: auth.user.id }); + } } } @@ -290,6 +299,26 @@ export class AssetService extends BaseService { if (Object.keys(writes).length > 0) { await this.assetRepository.upsertExif({ assetId: id, ...writes }); await this.jobRepository.queue({ name: JobName.SIDECAR_WRITE, data: { id, ...writes } }); + return true; } + return false; + } + + private async updateAllMetadata( + ids: string[], + dto: Pick, + ) { + const { description, dateTimeOriginal, latitude, longitude, rating } = dto; + const writes = _.omitBy({ description, dateTimeOriginal, latitude, longitude, rating }, _.isUndefined); + if (Object.keys(writes).length > 0) { + await this.assetRepository.updateAllExif(ids, writes); + const jobs: JobItem[] = ids.map((id) => ({ + name: JobName.SIDECAR_WRITE, + data: { id, ...writes }, + })); + await this.jobRepository.queueAll(jobs); + return true; + } + return false; } }