feat(server): Handle sidecars in external libraries (#14800)

* handle sidecars in external libraries

* don't add separate source
This commit is contained in:
Jonathan Jogenfors
2024-12-22 03:50:07 +01:00
committed by GitHub
parent 6080e6e827
commit 4bc2aa5451
5 changed files with 355 additions and 83 deletions

View File

@@ -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',
},
},
],

View File

@@ -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) {

View File

@@ -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;