mirror of
https://github.com/immich-app/immich.git
synced 2025-12-26 17:25:00 +03:00
feat: initial asset editing implementation
feat: db insertions for edits feat: get asset edits endpoint feat: wip apply edits feat: finish asset files changes feat: wip feat: wip fix: openapi fix: tests the failing tests were so scuffed. Simply solved by adding [] to the param list feat: more wip feat: more wip feat: some more tests and fixes chore: fix default for getting thumbnail and add todo for tests feat: LRTB validation chore: code cleanup chore: more test checks for cleanup feat: show edit pane fix: state issues chore: restructure web editor feat: restructure edit manager feat: refactor cropManager chore: combine all editing chore: web editing improvements fix: handling when no crops fix: openapi enum chore: more edit refactoring fix: make image decoding more efficient chore: more refactoring fix: getCrop LRTB algorithm fix: missing await chore: use relative coordinates for edit chore: update sql fix: use resize observer instead of svelte:doc resize hook chore: simplify quad box generation fix: light mode styling chore: refactor to not be a recursive job call this simplifies the logic and the job only completes once thumbhash and others are properly updated chore: more refactoring feat: use affine transforms for most operations feat: bounding box edit transformation feat: tests chore: sql and openapi sync fix: medium tests fix: rotated OCR chore: cleanup transform test fix: remove rebase issue fix(server): block edits for live photos, gifs, panoramic photos fix: openapi enum validation chore: rename edit endpoint chore: remove public modifiers feat: delete endpoint chore: use === and !== explicitly fix: require 1 edit for the editAsset endpoint fix: remove thumbnail edit notification and use on_upload_success instead fix: primary key on asset edit table chore: refactor to isPanorama chore: rename editRepository to assetEditRepository fix: missing toLowerCase fix: db migrations chore: update sql files
This commit is contained in:
203
server/test/fixtures/asset.stub.ts
vendored
203
server/test/fixtures/asset.stub.ts
vendored
@@ -1,5 +1,6 @@
|
||||
import { AssetFace, AssetFile, Exif } from 'src/database';
|
||||
import { MapAsset } from 'src/dtos/asset-response.dto';
|
||||
import { EditAction, EditActionItem } from 'src/dtos/editing.dto';
|
||||
import { AssetFileType, AssetStatus, AssetType, AssetVisibility } from 'src/enum';
|
||||
import { StorageAsset } from 'src/types';
|
||||
import { authStub } from 'test/fixtures/auth.stub';
|
||||
@@ -36,8 +37,35 @@ const sidecarFileWithoutExt: AssetFile = {
|
||||
path: '/original/path.xmp',
|
||||
};
|
||||
|
||||
const editedPreviewFile: AssetFile = {
|
||||
id: 'file-4',
|
||||
type: AssetFileType.EditedPreview,
|
||||
path: '/uploads/user-id/preview/path_edited.jpg',
|
||||
};
|
||||
|
||||
const editedThumbnailFile: AssetFile = {
|
||||
id: 'file-5',
|
||||
type: AssetFileType.EditedThumbnail,
|
||||
path: '/uploads/user-id/thumbnail/path_edited.jpg',
|
||||
};
|
||||
|
||||
const editedFullsizeFile: AssetFile = {
|
||||
id: 'file-6',
|
||||
type: AssetFileType.EditedFullSize,
|
||||
path: '/uploads/user-id/fullsize/path_edited.jpg',
|
||||
};
|
||||
|
||||
const files: AssetFile[] = [fullsizeFile, previewFile, thumbnailFile];
|
||||
|
||||
const editedFiles: AssetFile[] = [
|
||||
fullsizeFile,
|
||||
previewFile,
|
||||
thumbnailFile,
|
||||
editedFullsizeFile,
|
||||
editedPreviewFile,
|
||||
editedThumbnailFile,
|
||||
];
|
||||
|
||||
export const stackStub = (stackId: string, assets: (MapAsset & { exifInfo: Exif })[]) => {
|
||||
return {
|
||||
id: stackId,
|
||||
@@ -101,6 +129,9 @@ export const assetStub = {
|
||||
stackId: null,
|
||||
updateId: '42',
|
||||
visibility: AssetVisibility.Timeline,
|
||||
width: null,
|
||||
height: null,
|
||||
edits: [],
|
||||
}),
|
||||
|
||||
noWebpPath: Object.freeze({
|
||||
@@ -139,6 +170,9 @@ export const assetStub = {
|
||||
stackId: null,
|
||||
updateId: '42',
|
||||
visibility: AssetVisibility.Timeline,
|
||||
width: null,
|
||||
height: null,
|
||||
edits: [],
|
||||
}),
|
||||
|
||||
noThumbhash: Object.freeze({
|
||||
@@ -174,6 +208,9 @@ export const assetStub = {
|
||||
stackId: null,
|
||||
updateId: '42',
|
||||
visibility: AssetVisibility.Timeline,
|
||||
width: null,
|
||||
height: null,
|
||||
edits: [],
|
||||
}),
|
||||
|
||||
primaryImage: Object.freeze({
|
||||
@@ -219,6 +256,9 @@ export const assetStub = {
|
||||
updateId: '42',
|
||||
libraryId: null,
|
||||
visibility: AssetVisibility.Timeline,
|
||||
width: null,
|
||||
height: null,
|
||||
edits: [],
|
||||
}),
|
||||
|
||||
image: Object.freeze({
|
||||
@@ -264,6 +304,7 @@ export const assetStub = {
|
||||
height: null,
|
||||
width: null,
|
||||
visibility: AssetVisibility.Timeline,
|
||||
edits: [],
|
||||
}),
|
||||
|
||||
trashed: Object.freeze({
|
||||
@@ -304,6 +345,9 @@ export const assetStub = {
|
||||
stackId: null,
|
||||
updateId: '42',
|
||||
visibility: AssetVisibility.Timeline,
|
||||
width: null,
|
||||
height: null,
|
||||
edits: [],
|
||||
}),
|
||||
|
||||
trashedOffline: Object.freeze({
|
||||
@@ -344,6 +388,9 @@ export const assetStub = {
|
||||
stackId: null,
|
||||
updateId: '42',
|
||||
visibility: AssetVisibility.Timeline,
|
||||
width: null,
|
||||
height: null,
|
||||
edits: [],
|
||||
}),
|
||||
archived: Object.freeze({
|
||||
id: 'asset-id',
|
||||
@@ -383,6 +430,9 @@ export const assetStub = {
|
||||
stackId: null,
|
||||
updateId: '42',
|
||||
visibility: AssetVisibility.Timeline,
|
||||
width: null,
|
||||
height: null,
|
||||
edits: [],
|
||||
}),
|
||||
|
||||
external: Object.freeze({
|
||||
@@ -422,6 +472,9 @@ export const assetStub = {
|
||||
stackId: null,
|
||||
stack: null,
|
||||
visibility: AssetVisibility.Timeline,
|
||||
width: null,
|
||||
height: null,
|
||||
edits: [],
|
||||
}),
|
||||
|
||||
image1: Object.freeze({
|
||||
@@ -461,6 +514,9 @@ export const assetStub = {
|
||||
libraryId: null,
|
||||
stack: null,
|
||||
visibility: AssetVisibility.Timeline,
|
||||
width: null,
|
||||
height: null,
|
||||
edits: [],
|
||||
}),
|
||||
|
||||
imageFrom2015: Object.freeze({
|
||||
@@ -499,6 +555,9 @@ export const assetStub = {
|
||||
duplicateId: null,
|
||||
isOffline: false,
|
||||
visibility: AssetVisibility.Timeline,
|
||||
width: null,
|
||||
height: null,
|
||||
edits: [],
|
||||
}),
|
||||
|
||||
video: Object.freeze({
|
||||
@@ -539,6 +598,9 @@ export const assetStub = {
|
||||
libraryId: null,
|
||||
stackId: null,
|
||||
visibility: AssetVisibility.Timeline,
|
||||
width: null,
|
||||
height: null,
|
||||
edits: [],
|
||||
}),
|
||||
|
||||
livePhotoMotionAsset: Object.freeze({
|
||||
@@ -556,7 +618,10 @@ export const assetStub = {
|
||||
files: [] as AssetFile[],
|
||||
libraryId: null,
|
||||
visibility: AssetVisibility.Hidden,
|
||||
} as MapAsset & { faces: AssetFace[]; files: AssetFile[]; exifInfo: Exif }),
|
||||
width: null,
|
||||
height: null,
|
||||
edits: [] as EditActionItem[],
|
||||
} as MapAsset & { faces: AssetFace[]; files: AssetFile[]; exifInfo: Exif; edits: EditActionItem[] }),
|
||||
|
||||
livePhotoStillAsset: Object.freeze({
|
||||
id: 'live-photo-still-asset',
|
||||
@@ -574,7 +639,10 @@ export const assetStub = {
|
||||
files,
|
||||
faces: [] as AssetFace[],
|
||||
visibility: AssetVisibility.Timeline,
|
||||
} as MapAsset & { faces: AssetFace[]; files: AssetFile[] }),
|
||||
width: null,
|
||||
height: null,
|
||||
edits: [] as EditActionItem[],
|
||||
} as MapAsset & { faces: AssetFace[]; files: AssetFile[]; edits: EditActionItem[] }),
|
||||
|
||||
livePhotoWithOriginalFileName: Object.freeze({
|
||||
id: 'live-photo-still-asset',
|
||||
@@ -594,7 +662,10 @@ export const assetStub = {
|
||||
libraryId: null,
|
||||
faces: [] as AssetFace[],
|
||||
visibility: AssetVisibility.Timeline,
|
||||
} as MapAsset & { faces: AssetFace[]; files: AssetFile[] }),
|
||||
width: null,
|
||||
height: null,
|
||||
edits: [] as EditActionItem[],
|
||||
} as MapAsset & { faces: AssetFace[]; files: AssetFile[]; edits: EditActionItem[] }),
|
||||
|
||||
withLocation: Object.freeze({
|
||||
id: 'asset-with-favorite-id',
|
||||
@@ -638,6 +709,9 @@ export const assetStub = {
|
||||
isOffline: false,
|
||||
tags: [],
|
||||
visibility: AssetVisibility.Timeline,
|
||||
width: null,
|
||||
height: null,
|
||||
edits: [],
|
||||
}),
|
||||
|
||||
sidecar: Object.freeze({
|
||||
@@ -673,6 +747,9 @@ export const assetStub = {
|
||||
libraryId: null,
|
||||
stackId: null,
|
||||
visibility: AssetVisibility.Timeline,
|
||||
width: null,
|
||||
height: null,
|
||||
edits: [],
|
||||
}),
|
||||
|
||||
sidecarWithoutExt: Object.freeze({
|
||||
@@ -705,6 +782,9 @@ export const assetStub = {
|
||||
duplicateId: null,
|
||||
isOffline: false,
|
||||
visibility: AssetVisibility.Timeline,
|
||||
width: null,
|
||||
height: null,
|
||||
edits: [],
|
||||
}),
|
||||
|
||||
hasEncodedVideo: Object.freeze({
|
||||
@@ -744,6 +824,9 @@ export const assetStub = {
|
||||
stackId: null,
|
||||
stack: null,
|
||||
visibility: AssetVisibility.Timeline,
|
||||
width: null,
|
||||
height: null,
|
||||
edits: [],
|
||||
}),
|
||||
|
||||
hasFileExtension: Object.freeze({
|
||||
@@ -780,6 +863,9 @@ export const assetStub = {
|
||||
duplicateId: null,
|
||||
isOffline: false,
|
||||
visibility: AssetVisibility.Timeline,
|
||||
width: null,
|
||||
height: null,
|
||||
edits: [],
|
||||
}),
|
||||
|
||||
imageDng: Object.freeze({
|
||||
@@ -820,6 +906,9 @@ export const assetStub = {
|
||||
libraryId: null,
|
||||
stackId: null,
|
||||
visibility: AssetVisibility.Timeline,
|
||||
width: null,
|
||||
height: null,
|
||||
edits: [],
|
||||
}),
|
||||
|
||||
imageHif: Object.freeze({
|
||||
@@ -860,6 +949,9 @@ export const assetStub = {
|
||||
libraryId: null,
|
||||
stackId: null,
|
||||
visibility: AssetVisibility.Timeline,
|
||||
width: null,
|
||||
height: null,
|
||||
edits: [],
|
||||
}),
|
||||
panoramaTif: Object.freeze({
|
||||
id: 'asset-id',
|
||||
@@ -899,5 +991,110 @@ export const assetStub = {
|
||||
libraryId: null,
|
||||
stackId: null,
|
||||
visibility: AssetVisibility.Timeline,
|
||||
width: null,
|
||||
height: null,
|
||||
edits: [],
|
||||
}),
|
||||
withCropEdit: Object.freeze({
|
||||
id: 'asset-id',
|
||||
status: AssetStatus.Active,
|
||||
deviceAssetId: 'device-asset-id',
|
||||
fileModifiedAt: new Date('2023-02-23T05:06:29.716Z'),
|
||||
fileCreatedAt: new Date('2023-02-23T05:06:29.716Z'),
|
||||
owner: userStub.user1,
|
||||
ownerId: 'user-id',
|
||||
deviceId: 'device-id',
|
||||
originalPath: '/original/path.jpg',
|
||||
files,
|
||||
checksum: Buffer.from('file hash', 'utf8'),
|
||||
type: AssetType.Image,
|
||||
thumbhash: Buffer.from('blablabla', 'base64'),
|
||||
encodedVideoPath: null,
|
||||
createdAt: new Date('2023-02-23T05:06:29.716Z'),
|
||||
updatedAt: new Date('2023-02-23T05:06:29.716Z'),
|
||||
localDateTime: new Date('2025-01-01T01:02:03.456Z'),
|
||||
isFavorite: true,
|
||||
duration: null,
|
||||
isExternal: false,
|
||||
livePhotoVideo: null,
|
||||
livePhotoVideoId: null,
|
||||
updateId: 'foo',
|
||||
libraryId: null,
|
||||
stackId: null,
|
||||
sharedLinks: [],
|
||||
originalFileName: 'asset-id.jpg',
|
||||
faces: [],
|
||||
deletedAt: null,
|
||||
sidecarPath: null,
|
||||
exifInfo: {
|
||||
fileSizeInByte: 5000,
|
||||
exifImageHeight: 3840,
|
||||
exifImageWidth: 2160,
|
||||
} as Exif,
|
||||
duplicateId: null,
|
||||
isOffline: false,
|
||||
stack: null,
|
||||
orientation: '',
|
||||
projectionType: null,
|
||||
height: 3840,
|
||||
width: 2160,
|
||||
visibility: AssetVisibility.Timeline,
|
||||
edits: [
|
||||
{
|
||||
action: EditAction.Crop,
|
||||
parameters: {
|
||||
width: 1512,
|
||||
height: 1152,
|
||||
x: 216,
|
||||
y: 1512,
|
||||
},
|
||||
},
|
||||
] as EditActionItem[],
|
||||
}),
|
||||
withRevertedEdits: Object.freeze({
|
||||
id: 'asset-id',
|
||||
status: AssetStatus.Active,
|
||||
deviceAssetId: 'device-asset-id',
|
||||
fileModifiedAt: new Date('2023-02-23T05:06:29.716Z'),
|
||||
fileCreatedAt: new Date('2023-02-23T05:06:29.716Z'),
|
||||
owner: userStub.user1,
|
||||
ownerId: 'user-id',
|
||||
deviceId: 'device-id',
|
||||
originalPath: '/original/path.jpg',
|
||||
files: editedFiles,
|
||||
checksum: Buffer.from('file hash', 'utf8'),
|
||||
type: AssetType.Image,
|
||||
thumbhash: Buffer.from('blablabla', 'base64'),
|
||||
encodedVideoPath: null,
|
||||
createdAt: new Date('2023-02-23T05:06:29.716Z'),
|
||||
updatedAt: new Date('2023-02-23T05:06:29.716Z'),
|
||||
localDateTime: new Date('2025-01-01T01:02:03.456Z'),
|
||||
isFavorite: true,
|
||||
duration: null,
|
||||
isExternal: false,
|
||||
livePhotoVideo: null,
|
||||
livePhotoVideoId: null,
|
||||
updateId: 'foo',
|
||||
libraryId: null,
|
||||
stackId: null,
|
||||
sharedLinks: [],
|
||||
originalFileName: 'asset-id.jpg',
|
||||
faces: [],
|
||||
deletedAt: null,
|
||||
sidecarPath: null,
|
||||
exifInfo: {
|
||||
fileSizeInByte: 5000,
|
||||
exifImageHeight: 3840,
|
||||
exifImageWidth: 2160,
|
||||
} as Exif,
|
||||
duplicateId: null,
|
||||
isOffline: false,
|
||||
stack: null,
|
||||
orientation: '',
|
||||
projectionType: null,
|
||||
height: 3840,
|
||||
width: 2160,
|
||||
visibility: AssetVisibility.Timeline,
|
||||
edits: [],
|
||||
}),
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user