mirror of
https://github.com/immich-app/immich.git
synced 2025-02-04 18:35:31 +02:00
fix(server): link motion photo with existing video asset (#8724)
* added motion photo linking * added tests
This commit is contained in:
parent
ec76e5ef23
commit
58346465aa
@ -439,7 +439,7 @@ describe(MetadataService.name, () => {
|
|||||||
});
|
});
|
||||||
cryptoRepository.hashSha1.mockReturnValue(randomBytes(512));
|
cryptoRepository.hashSha1.mockReturnValue(randomBytes(512));
|
||||||
assetMock.getByChecksum.mockResolvedValue(null);
|
assetMock.getByChecksum.mockResolvedValue(null);
|
||||||
assetMock.create.mockResolvedValue(assetStub.livePhotoMotionAsset);
|
assetMock.create.mockImplementation((asset) => Promise.resolve({ ...assetStub.livePhotoMotionAsset, ...asset }));
|
||||||
|
|
||||||
await sut.handleMetadataExtraction({ id: assetStub.livePhotoStillAsset.id });
|
await sut.handleMetadataExtraction({ id: assetStub.livePhotoStillAsset.id });
|
||||||
expect(jobMock.queue).toHaveBeenNthCalledWith(2, {
|
expect(jobMock.queue).toHaveBeenNthCalledWith(2, {
|
||||||
@ -467,6 +467,30 @@ describe(MetadataService.name, () => {
|
|||||||
expect(jobMock.queue).toHaveBeenCalledTimes(0);
|
expect(jobMock.queue).toHaveBeenCalledTimes(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should link and hide motion video asset to still asset if the hash of the extracted video matches an existing asset', async () => {
|
||||||
|
assetMock.getByIds.mockResolvedValue([{ ...assetStub.livePhotoStillAsset, livePhotoVideoId: null }]);
|
||||||
|
metadataMock.readTags.mockResolvedValue({
|
||||||
|
Directory: 'foo/bar/',
|
||||||
|
MotionPhoto: 1,
|
||||||
|
MicroVideo: 1,
|
||||||
|
MicroVideoOffset: 1,
|
||||||
|
});
|
||||||
|
cryptoRepository.hashSha1.mockReturnValue(randomBytes(512));
|
||||||
|
assetMock.getByChecksum.mockResolvedValue({ ...assetStub.livePhotoMotionAsset, isVisible: true });
|
||||||
|
const video = randomBytes(512);
|
||||||
|
storageMock.readFile.mockResolvedValue(video);
|
||||||
|
|
||||||
|
await sut.handleMetadataExtraction({ id: assetStub.livePhotoStillAsset.id });
|
||||||
|
expect(assetMock.update).toHaveBeenNthCalledWith(1, {
|
||||||
|
id: assetStub.livePhotoMotionAsset.id,
|
||||||
|
isVisible: false,
|
||||||
|
});
|
||||||
|
expect(assetMock.update).toHaveBeenNthCalledWith(2, {
|
||||||
|
id: assetStub.livePhotoStillAsset.id,
|
||||||
|
livePhotoVideoId: assetStub.livePhotoMotionAsset.id,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should save all metadata', async () => {
|
it('should save all metadata', async () => {
|
||||||
const tags: ImmichTags = {
|
const tags: ImmichTags = {
|
||||||
BitsPerSample: 1,
|
BitsPerSample: 1,
|
||||||
|
@ -412,6 +412,12 @@ export class MetadataService {
|
|||||||
'base64',
|
'base64',
|
||||||
)} already exists in the repository`,
|
)} already exists in the repository`,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Hide the motion photo video asset if it's not already hidden to prepare for linking
|
||||||
|
if (motionAsset.isVisible) {
|
||||||
|
await this.assetRepository.update({ id: motionAsset.id, isVisible: false });
|
||||||
|
this.logger.log(`Hid unlinked motion photo video asset (${motionAsset.id})`);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// We create a UUID in advance so that each extracted video can have a unique filename
|
// We create a UUID in advance so that each extracted video can have a unique filename
|
||||||
// (allowing us to delete old ones if necessary)
|
// (allowing us to delete old ones if necessary)
|
||||||
@ -438,11 +444,14 @@ export class MetadataService {
|
|||||||
this.storageCore.ensureFolders(motionPath);
|
this.storageCore.ensureFolders(motionPath);
|
||||||
await this.storageRepository.writeFile(motionAsset.originalPath, video);
|
await this.storageRepository.writeFile(motionAsset.originalPath, video);
|
||||||
await this.jobRepository.queue({ name: JobName.METADATA_EXTRACTION, data: { id: motionAsset.id } });
|
await this.jobRepository.queue({ name: JobName.METADATA_EXTRACTION, data: { id: motionAsset.id } });
|
||||||
await this.assetRepository.update({ id: asset.id, livePhotoVideoId: motionAsset.id });
|
}
|
||||||
|
|
||||||
|
if (asset.livePhotoVideoId !== motionAsset.id) {
|
||||||
|
await this.assetRepository.update({ id: asset.id, livePhotoVideoId: motionAsset.id });
|
||||||
// If the asset already had an associated livePhotoVideo, delete it, because
|
// If the asset already had an associated livePhotoVideo, delete it, because
|
||||||
// its checksum doesn't match the checksum of the motionAsset we just extracted
|
// its checksum doesn't match the checksum of the motionAsset we just extracted
|
||||||
// (if it did, getByChecksum() would've returned non-null)
|
// (if it did, getByChecksum() would've returned a motionAsset with the same ID as livePhotoVideoId)
|
||||||
|
// note asset.livePhotoVideoId is not motionAsset.id yet
|
||||||
if (asset.livePhotoVideoId) {
|
if (asset.livePhotoVideoId) {
|
||||||
await this.jobRepository.queue({ name: JobName.ASSET_DELETION, data: { id: asset.livePhotoVideoId } });
|
await this.jobRepository.queue({ name: JobName.ASSET_DELETION, data: { id: asset.livePhotoVideoId } });
|
||||||
this.logger.log(`Removed old motion photo video asset (${asset.livePhotoVideoId})`);
|
this.logger.log(`Removed old motion photo video asset (${asset.livePhotoVideoId})`);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user