diff --git a/server/src/services/metadata.service.spec.ts b/server/src/services/metadata.service.spec.ts index c0758eeeaf..fefd40becb 100644 --- a/server/src/services/metadata.service.spec.ts +++ b/server/src/services/metadata.service.spec.ts @@ -459,10 +459,14 @@ describe(MetadataService.name, () => { storageMock.readFile.mockResolvedValue(video); await sut.handleMetadataExtraction({ id: assetStub.livePhotoStillAsset.id }); - expect(jobMock.queue).toHaveBeenNthCalledWith(2, { + expect(jobMock.queue).toHaveBeenNthCalledWith(1, { name: JobName.ASSET_DELETION, data: { id: assetStub.livePhotoStillAsset.livePhotoVideoId }, }); + expect(jobMock.queue).toHaveBeenNthCalledWith(2, { + name: JobName.METADATA_EXTRACTION, + data: { id: 'random-uuid' }, + }); }); it('should not create a new motion photo video asset if the hash of the extracted video matches an existing asset', async () => { @@ -477,6 +481,7 @@ describe(MetadataService.name, () => { assetMock.getByChecksum.mockResolvedValue(assetStub.livePhotoMotionAsset); const video = randomBytes(512); storageMock.readFile.mockResolvedValue(video); + storageMock.checkFileExists.mockResolvedValue(true); await sut.handleMetadataExtraction({ id: assetStub.livePhotoStillAsset.id }); expect(assetMock.create).toHaveBeenCalledTimes(0); diff --git a/server/src/services/metadata.service.ts b/server/src/services/metadata.service.ts index 4b013fb663..0e5395c5bd 100644 --- a/server/src/services/metadata.service.ts +++ b/server/src/services/metadata.service.ts @@ -423,10 +423,7 @@ export class MetadataService { this.logger.log(`Hid unlinked motion photo video asset (${motionAsset.id})`); } } else { - // We create a UUID in advance so that each extracted video can have a unique filename - // (allowing us to delete old ones if necessary) const motionAssetId = this.cryptoRepository.randomUUID(); - const motionPath = StorageCore.getAndroidMotionPath(asset, motionAssetId); const createdAt = asset.fileCreatedAt ?? asset.createdAt; motionAsset = await this.assetRepository.create({ id: motionAssetId, @@ -437,16 +434,13 @@ export class MetadataService { localDateTime: createdAt, checksum, ownerId: asset.ownerId, - originalPath: motionPath, + originalPath: StorageCore.getAndroidMotionPath(asset, motionAssetId), originalFileName: asset.originalFileName, isVisible: false, deviceAssetId: 'NONE', deviceId: 'NONE', }); - this.storageCore.ensureFolders(motionPath); - await this.storageRepository.writeFile(motionAsset.originalPath, video); - await this.jobRepository.queue({ name: JobName.METADATA_EXTRACTION, data: { id: motionAsset.id } }); if (!asset.isExternal) { await this.userRepository.updateUsage(asset.ownerId, video.byteLength); } @@ -465,6 +459,15 @@ export class MetadataService { } } + // write extracted motion video to disk, especially if the encoded-video folder has been deleted + const existsOnDisk = await this.storageRepository.checkFileExists(motionAsset.originalPath); + if (!existsOnDisk) { + this.storageCore.ensureFolders(motionAsset.originalPath); + await this.storageRepository.writeFile(motionAsset.originalPath, video); + this.logger.log(`Wrote motion photo video to ${motionAsset.originalPath}`); + await this.jobRepository.queue({ name: JobName.METADATA_EXTRACTION, data: { id: motionAsset.id } }); + } + this.logger.debug(`Finished motion photo video extraction (${asset.id})`); } catch (error: Error | any) { this.logger.error(`Failed to extract live photo ${asset.originalPath}: ${error}`, error?.stack);