mirror of
https://github.com/immich-app/immich.git
synced 2025-12-20 01:11:46 +03:00
feat(server): Handle sidecars in external libraries (#14800)
* handle sidecars in external libraries * don't add separate source
This commit is contained in:
committed by
GitHub
parent
6080e6e827
commit
4bc2aa5451
@@ -414,7 +414,6 @@ describe(LibraryService.name, () => {
|
||||
localDateTime: expect.any(Date),
|
||||
type: AssetType.IMAGE,
|
||||
originalFileName: 'photo.jpg',
|
||||
sidecarPath: null,
|
||||
isExternal: true,
|
||||
},
|
||||
],
|
||||
@@ -423,57 +422,9 @@ describe(LibraryService.name, () => {
|
||||
expect(jobMock.queue.mock.calls).toEqual([
|
||||
[
|
||||
{
|
||||
name: JobName.METADATA_EXTRACTION,
|
||||
name: JobName.SIDECAR_DISCOVERY,
|
||||
data: {
|
||||
id: assetStub.image.id,
|
||||
source: 'upload',
|
||||
},
|
||||
},
|
||||
],
|
||||
]);
|
||||
});
|
||||
|
||||
it('should import a new asset with sidecar', async () => {
|
||||
const mockLibraryJob: ILibraryFileJob = {
|
||||
id: libraryStub.externalLibrary1.id,
|
||||
ownerId: mockUser.id,
|
||||
assetPath: '/data/user1/photo.jpg',
|
||||
};
|
||||
|
||||
assetMock.getByLibraryIdAndOriginalPath.mockResolvedValue(null);
|
||||
assetMock.create.mockResolvedValue(assetStub.image);
|
||||
storageMock.checkFileExists.mockResolvedValue(true);
|
||||
libraryMock.get.mockResolvedValue(libraryStub.externalLibrary1);
|
||||
|
||||
await expect(sut.handleSyncFile(mockLibraryJob)).resolves.toBe(JobStatus.SUCCESS);
|
||||
|
||||
expect(assetMock.create.mock.calls).toEqual([
|
||||
[
|
||||
{
|
||||
ownerId: mockUser.id,
|
||||
libraryId: libraryStub.externalLibrary1.id,
|
||||
checksum: expect.any(Buffer),
|
||||
originalPath: '/data/user1/photo.jpg',
|
||||
deviceAssetId: expect.any(String),
|
||||
deviceId: 'Library Import',
|
||||
fileCreatedAt: expect.any(Date),
|
||||
fileModifiedAt: expect.any(Date),
|
||||
localDateTime: expect.any(Date),
|
||||
type: AssetType.IMAGE,
|
||||
originalFileName: 'photo.jpg',
|
||||
sidecarPath: '/data/user1/photo.jpg.xmp',
|
||||
isExternal: true,
|
||||
},
|
||||
],
|
||||
]);
|
||||
|
||||
expect(jobMock.queue.mock.calls).toEqual([
|
||||
[
|
||||
{
|
||||
name: JobName.METADATA_EXTRACTION,
|
||||
data: {
|
||||
id: assetStub.image.id,
|
||||
source: 'upload',
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -507,7 +458,6 @@ describe(LibraryService.name, () => {
|
||||
localDateTime: expect.any(Date),
|
||||
type: AssetType.VIDEO,
|
||||
originalFileName: 'video.mp4',
|
||||
sidecarPath: null,
|
||||
isExternal: true,
|
||||
},
|
||||
],
|
||||
@@ -516,10 +466,9 @@ describe(LibraryService.name, () => {
|
||||
expect(jobMock.queue.mock.calls).toEqual([
|
||||
[
|
||||
{
|
||||
name: JobName.METADATA_EXTRACTION,
|
||||
name: JobName.SIDECAR_DISCOVERY,
|
||||
data: {
|
||||
id: assetStub.image.id,
|
||||
source: 'upload',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
@@ -396,12 +396,6 @@ export class LibraryService extends BaseService {
|
||||
|
||||
const pathHash = this.cryptoRepository.hashSha1(`path:${assetPath}`);
|
||||
|
||||
// TODO: doesn't xmp replace the file extension? Will need investigation
|
||||
let sidecarPath: string | null = null;
|
||||
if (await this.storageRepository.checkFileExists(`${assetPath}.xmp`, R_OK)) {
|
||||
sidecarPath = `${assetPath}.xmp`;
|
||||
}
|
||||
|
||||
const assetType = mimeTypes.isVideo(assetPath) ? AssetType.VIDEO : AssetType.IMAGE;
|
||||
|
||||
const mtime = stat.mtime;
|
||||
@@ -418,8 +412,6 @@ export class LibraryService extends BaseService {
|
||||
localDateTime: mtime,
|
||||
type: assetType,
|
||||
originalFileName: parse(assetPath).base,
|
||||
|
||||
sidecarPath,
|
||||
isExternal: true,
|
||||
});
|
||||
|
||||
@@ -431,7 +423,11 @@ export class LibraryService extends BaseService {
|
||||
async queuePostSyncJobs(asset: AssetEntity) {
|
||||
this.logger.debug(`Queueing metadata extraction for: ${asset.originalPath}`);
|
||||
|
||||
await this.jobRepository.queue({ name: JobName.METADATA_EXTRACTION, data: { id: asset.id, source: 'upload' } });
|
||||
// We queue a sidecar discovery which, in turn, queues metadata extraction
|
||||
await this.jobRepository.queue({
|
||||
name: JobName.SIDECAR_DISCOVERY,
|
||||
data: { id: asset.id },
|
||||
});
|
||||
}
|
||||
|
||||
async queueScan(id: string) {
|
||||
|
||||
@@ -698,7 +698,7 @@ export class MetadataService extends BaseService {
|
||||
return JobStatus.FAILED;
|
||||
}
|
||||
|
||||
if (!isSync && (!asset.isVisible || asset.sidecarPath)) {
|
||||
if (!isSync && (!asset.isVisible || asset.sidecarPath) && !asset.isExternal) {
|
||||
return JobStatus.FAILED;
|
||||
}
|
||||
|
||||
@@ -720,6 +720,13 @@ export class MetadataService extends BaseService {
|
||||
sidecarPath = sidecarPathWithoutExt;
|
||||
}
|
||||
|
||||
if (asset.isExternal) {
|
||||
if (sidecarPath !== asset.sidecarPath) {
|
||||
await this.assetRepository.update({ id: asset.id, sidecarPath });
|
||||
}
|
||||
return JobStatus.SUCCESS;
|
||||
}
|
||||
|
||||
if (sidecarPath) {
|
||||
await this.assetRepository.update({ id: asset.id, sidecarPath });
|
||||
return JobStatus.SUCCESS;
|
||||
|
||||
Reference in New Issue
Block a user