mirror of
https://github.com/immich-app/immich.git
synced 2026-05-22 15:42:32 -04:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8c02edcdb7 | |||
| f16a83f65b | |||
| 2325a359a6 |
@@ -566,20 +566,6 @@ describe('/asset', () => {
|
|||||||
expect(status).toEqual(200);
|
expect(status).toEqual(200);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set the negative rating', async () => {
|
|
||||||
const { status, body } = await request(app)
|
|
||||||
.put(`/assets/${user1Assets[0].id}`)
|
|
||||||
.set('Authorization', `Bearer ${user1.accessToken}`)
|
|
||||||
.send({ rating: -1 });
|
|
||||||
expect(body).toMatchObject({
|
|
||||||
id: user1Assets[0].id,
|
|
||||||
exifInfo: expect.objectContaining({
|
|
||||||
rating: -1,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
expect(status).toEqual(200);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return tagged people', async () => {
|
it('should return tagged people', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.put(`/assets/${user1Assets[0].id}`)
|
.put(`/assets/${user1Assets[0].id}`)
|
||||||
|
|||||||
+1
-1
@@ -97,7 +97,7 @@ class AssetBulkUpdateDto {
|
|||||||
|
|
||||||
/// Rating in range [1-5], or null for unrated
|
/// Rating in range [1-5], or null for unrated
|
||||||
///
|
///
|
||||||
/// Minimum value: -1
|
/// Minimum value: 0
|
||||||
/// Maximum value: 5
|
/// Maximum value: 5
|
||||||
int? rating;
|
int? rating;
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -108,8 +108,8 @@ class ExifResponseDto {
|
|||||||
|
|
||||||
/// Rating
|
/// Rating
|
||||||
///
|
///
|
||||||
/// Minimum value: -9007199254740991
|
/// Minimum value: 1
|
||||||
/// Maximum value: 9007199254740991
|
/// Maximum value: 5
|
||||||
int? rating;
|
int? rating;
|
||||||
|
|
||||||
/// State/province name
|
/// State/province name
|
||||||
|
|||||||
+1
-1
@@ -238,7 +238,7 @@ class MetadataSearchDto {
|
|||||||
|
|
||||||
/// Filter by rating [1-5], or null for unrated
|
/// Filter by rating [1-5], or null for unrated
|
||||||
///
|
///
|
||||||
/// Minimum value: -1
|
/// Minimum value: 0
|
||||||
/// Maximum value: 5
|
/// Maximum value: 5
|
||||||
int? rating;
|
int? rating;
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -145,7 +145,7 @@ class RandomSearchDto {
|
|||||||
|
|
||||||
/// Filter by rating [1-5], or null for unrated
|
/// Filter by rating [1-5], or null for unrated
|
||||||
///
|
///
|
||||||
/// Minimum value: -1
|
/// Minimum value: 0
|
||||||
/// Maximum value: 5
|
/// Maximum value: 5
|
||||||
int? rating;
|
int? rating;
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -186,7 +186,7 @@ class SmartSearchDto {
|
|||||||
|
|
||||||
/// Filter by rating [1-5], or null for unrated
|
/// Filter by rating [1-5], or null for unrated
|
||||||
///
|
///
|
||||||
/// Minimum value: -1
|
/// Minimum value: 0
|
||||||
/// Maximum value: 5
|
/// Maximum value: 5
|
||||||
int? rating;
|
int? rating;
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -150,7 +150,7 @@ class StatisticsSearchDto {
|
|||||||
|
|
||||||
/// Filter by rating [1-5], or null for unrated
|
/// Filter by rating [1-5], or null for unrated
|
||||||
///
|
///
|
||||||
/// Minimum value: -1
|
/// Minimum value: 0
|
||||||
/// Maximum value: 5
|
/// Maximum value: 5
|
||||||
int? rating;
|
int? rating;
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -79,7 +79,7 @@ class UpdateAssetDto {
|
|||||||
|
|
||||||
/// Rating in range [1-5], or null for unrated
|
/// Rating in range [1-5], or null for unrated
|
||||||
///
|
///
|
||||||
/// Minimum value: -1
|
/// Minimum value: 0
|
||||||
/// Maximum value: 5
|
/// Maximum value: 5
|
||||||
int? rating;
|
int? rating;
|
||||||
|
|
||||||
|
|||||||
@@ -9369,12 +9369,17 @@
|
|||||||
"version": "v2.6.0",
|
"version": "v2.6.0",
|
||||||
"state": "Updated",
|
"state": "Updated",
|
||||||
"description": "Using -1 as a rating is deprecated and will be removed in the next major version."
|
"description": "Using -1 as a rating is deprecated and will be removed in the next major version."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "v3",
|
||||||
|
"state": "Updated",
|
||||||
|
"description": "Using -1 as a rating is no longer valid."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"x-immich-state": "Stable",
|
"x-immich-state": "Stable",
|
||||||
"schema": {
|
"schema": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"minimum": -1,
|
"minimum": 0,
|
||||||
"maximum": 5,
|
"maximum": 5,
|
||||||
"nullable": true
|
"nullable": true
|
||||||
}
|
}
|
||||||
@@ -15678,7 +15683,7 @@
|
|||||||
"rating": {
|
"rating": {
|
||||||
"description": "Rating in range [1-5], or null for unrated",
|
"description": "Rating in range [1-5], or null for unrated",
|
||||||
"maximum": 5,
|
"maximum": 5,
|
||||||
"minimum": -1,
|
"minimum": 0,
|
||||||
"nullable": true,
|
"nullable": true,
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"x-immich-history": [
|
"x-immich-history": [
|
||||||
@@ -15694,6 +15699,11 @@
|
|||||||
"version": "v2.6.0",
|
"version": "v2.6.0",
|
||||||
"state": "Updated",
|
"state": "Updated",
|
||||||
"description": "Using -1 as a rating is deprecated and will be removed in the next major version."
|
"description": "Using -1 as a rating is deprecated and will be removed in the next major version."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "v3",
|
||||||
|
"state": "Updated",
|
||||||
|
"description": "Using -1 as a rating is no longer valid."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"x-immich-state": "Stable"
|
"x-immich-state": "Stable"
|
||||||
@@ -17738,8 +17748,8 @@
|
|||||||
"rating": {
|
"rating": {
|
||||||
"default": null,
|
"default": null,
|
||||||
"description": "Rating",
|
"description": "Rating",
|
||||||
"maximum": 9007199254740991,
|
"maximum": 5,
|
||||||
"minimum": -9007199254740991,
|
"minimum": 1,
|
||||||
"nullable": true,
|
"nullable": true,
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
@@ -18769,7 +18779,7 @@
|
|||||||
"rating": {
|
"rating": {
|
||||||
"description": "Filter by rating [1-5], or null for unrated",
|
"description": "Filter by rating [1-5], or null for unrated",
|
||||||
"maximum": 5,
|
"maximum": 5,
|
||||||
"minimum": -1,
|
"minimum": 0,
|
||||||
"nullable": true,
|
"nullable": true,
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"x-immich-history": [
|
"x-immich-history": [
|
||||||
@@ -18785,6 +18795,11 @@
|
|||||||
"version": "v2.6.0",
|
"version": "v2.6.0",
|
||||||
"state": "Updated",
|
"state": "Updated",
|
||||||
"description": "Using -1 as a rating is deprecated and will be removed in the next major version."
|
"description": "Using -1 as a rating is deprecated and will be removed in the next major version."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "v3",
|
||||||
|
"state": "Updated",
|
||||||
|
"description": "Using -1 as a rating is no longer valid."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"x-immich-state": "Stable"
|
"x-immich-state": "Stable"
|
||||||
@@ -20622,7 +20637,7 @@
|
|||||||
"rating": {
|
"rating": {
|
||||||
"description": "Filter by rating [1-5], or null for unrated",
|
"description": "Filter by rating [1-5], or null for unrated",
|
||||||
"maximum": 5,
|
"maximum": 5,
|
||||||
"minimum": -1,
|
"minimum": 0,
|
||||||
"nullable": true,
|
"nullable": true,
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"x-immich-history": [
|
"x-immich-history": [
|
||||||
@@ -20638,6 +20653,11 @@
|
|||||||
"version": "v2.6.0",
|
"version": "v2.6.0",
|
||||||
"state": "Updated",
|
"state": "Updated",
|
||||||
"description": "Using -1 as a rating is deprecated and will be removed in the next major version."
|
"description": "Using -1 as a rating is deprecated and will be removed in the next major version."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "v3",
|
||||||
|
"state": "Updated",
|
||||||
|
"description": "Using -1 as a rating is no longer valid."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"x-immich-state": "Stable"
|
"x-immich-state": "Stable"
|
||||||
@@ -22006,7 +22026,7 @@
|
|||||||
"rating": {
|
"rating": {
|
||||||
"description": "Filter by rating [1-5], or null for unrated",
|
"description": "Filter by rating [1-5], or null for unrated",
|
||||||
"maximum": 5,
|
"maximum": 5,
|
||||||
"minimum": -1,
|
"minimum": 0,
|
||||||
"nullable": true,
|
"nullable": true,
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"x-immich-history": [
|
"x-immich-history": [
|
||||||
@@ -22022,6 +22042,11 @@
|
|||||||
"version": "v2.6.0",
|
"version": "v2.6.0",
|
||||||
"state": "Updated",
|
"state": "Updated",
|
||||||
"description": "Using -1 as a rating is deprecated and will be removed in the next major version."
|
"description": "Using -1 as a rating is deprecated and will be removed in the next major version."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "v3",
|
||||||
|
"state": "Updated",
|
||||||
|
"description": "Using -1 as a rating is no longer valid."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"x-immich-state": "Stable"
|
"x-immich-state": "Stable"
|
||||||
@@ -22266,7 +22291,7 @@
|
|||||||
"rating": {
|
"rating": {
|
||||||
"description": "Filter by rating [1-5], or null for unrated",
|
"description": "Filter by rating [1-5], or null for unrated",
|
||||||
"maximum": 5,
|
"maximum": 5,
|
||||||
"minimum": -1,
|
"minimum": 0,
|
||||||
"nullable": true,
|
"nullable": true,
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"x-immich-history": [
|
"x-immich-history": [
|
||||||
@@ -22282,6 +22307,11 @@
|
|||||||
"version": "v2.6.0",
|
"version": "v2.6.0",
|
||||||
"state": "Updated",
|
"state": "Updated",
|
||||||
"description": "Using -1 as a rating is deprecated and will be removed in the next major version."
|
"description": "Using -1 as a rating is deprecated and will be removed in the next major version."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "v3",
|
||||||
|
"state": "Updated",
|
||||||
|
"description": "Using -1 as a rating is no longer valid."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"x-immich-state": "Stable"
|
"x-immich-state": "Stable"
|
||||||
@@ -25277,7 +25307,7 @@
|
|||||||
"rating": {
|
"rating": {
|
||||||
"description": "Rating in range [1-5], or null for unrated",
|
"description": "Rating in range [1-5], or null for unrated",
|
||||||
"maximum": 5,
|
"maximum": 5,
|
||||||
"minimum": -1,
|
"minimum": 0,
|
||||||
"nullable": true,
|
"nullable": true,
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"x-immich-history": [
|
"x-immich-history": [
|
||||||
@@ -25293,6 +25323,11 @@
|
|||||||
"version": "v2.6.0",
|
"version": "v2.6.0",
|
||||||
"state": "Updated",
|
"state": "Updated",
|
||||||
"description": "Using -1 as a rating is deprecated and will be removed in the next major version."
|
"description": "Using -1 as a rating is deprecated and will be removed in the next major version."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "v3",
|
||||||
|
"state": "Updated",
|
||||||
|
"description": "Using -1 as a rating is no longer valid."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"x-immich-state": "Stable"
|
"x-immich-state": "Stable"
|
||||||
|
|||||||
@@ -230,7 +230,7 @@ describe(AssetController.name, () => {
|
|||||||
|
|
||||||
it('should leave correct ratings as-is', async () => {
|
it('should leave correct ratings as-is', async () => {
|
||||||
const assetId = factory.uuid();
|
const assetId = factory.uuid();
|
||||||
for (const test of [{ rating: -1 }, { rating: 1 }, { rating: 5 }]) {
|
for (const test of [{ rating: 1 }, { rating: 5 }]) {
|
||||||
const { status } = await request(ctx.getHttpServer()).put(`/assets/${assetId}`).send(test);
|
const { status } = await request(ctx.getHttpServer()).put(`/assets/${assetId}`).send(test);
|
||||||
expect(service.update).toHaveBeenCalledWith(undefined, assetId, test);
|
expect(service.update).toHaveBeenCalledWith(undefined, assetId, test);
|
||||||
expect(status).toBe(200);
|
expect(status).toBe(200);
|
||||||
|
|||||||
@@ -14,9 +14,8 @@ const UpdateAssetBaseSchema = z
|
|||||||
latitude: latitudeSchema.optional().describe('Latitude coordinate'),
|
latitude: latitudeSchema.optional().describe('Latitude coordinate'),
|
||||||
longitude: longitudeSchema.optional().describe('Longitude coordinate'),
|
longitude: longitudeSchema.optional().describe('Longitude coordinate'),
|
||||||
rating: z
|
rating: z
|
||||||
.number()
|
|
||||||
.int()
|
.int()
|
||||||
.min(-1)
|
.min(0)
|
||||||
.max(5)
|
.max(5)
|
||||||
.transform((value) => (value === 0 ? null : value))
|
.transform((value) => (value === 0 ? null : value))
|
||||||
.nullish()
|
.nullish()
|
||||||
@@ -26,6 +25,7 @@ const UpdateAssetBaseSchema = z
|
|||||||
.added('v1')
|
.added('v1')
|
||||||
.stable('v2')
|
.stable('v2')
|
||||||
.updated('v2.6.0', 'Using -1 as a rating is deprecated and will be removed in the next major version.')
|
.updated('v2.6.0', 'Using -1 as a rating is deprecated and will be removed in the next major version.')
|
||||||
|
.updated('v3', 'Using -1 as a rating is no longer valid.')
|
||||||
.getExtensions(),
|
.getExtensions(),
|
||||||
}),
|
}),
|
||||||
description: z.string().optional().describe('Asset description'),
|
description: z.string().optional().describe('Asset description'),
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export const ExifResponseSchema = z
|
|||||||
country: z.string().nullish().default(null).describe('Country name'),
|
country: z.string().nullish().default(null).describe('Country name'),
|
||||||
description: z.string().nullish().default(null).describe('Image description'),
|
description: z.string().nullish().default(null).describe('Image description'),
|
||||||
projectionType: z.string().nullish().default(null).describe('Projection type'),
|
projectionType: z.string().nullish().default(null).describe('Projection type'),
|
||||||
rating: z.int().nullish().default(null).describe('Rating'),
|
rating: z.int().min(1).max(5).nullish().default(null).describe('Rating'),
|
||||||
})
|
})
|
||||||
.describe('EXIF response')
|
.describe('EXIF response')
|
||||||
.meta({ id: 'ExifResponseDto' });
|
.meta({ id: 'ExifResponseDto' });
|
||||||
|
|||||||
@@ -35,8 +35,9 @@ const BaseSearchSchema = z.object({
|
|||||||
albumIds: z.array(z.uuidv4()).optional().describe('Filter by album IDs'),
|
albumIds: z.array(z.uuidv4()).optional().describe('Filter by album IDs'),
|
||||||
rating: z
|
rating: z
|
||||||
.int()
|
.int()
|
||||||
.min(-1)
|
.min(0)
|
||||||
.max(5)
|
.max(5)
|
||||||
|
.transform((value) => (value === 0 ? null : value))
|
||||||
.nullish()
|
.nullish()
|
||||||
.describe('Filter by rating [1-5], or null for unrated')
|
.describe('Filter by rating [1-5], or null for unrated')
|
||||||
.meta({
|
.meta({
|
||||||
@@ -44,6 +45,7 @@ const BaseSearchSchema = z.object({
|
|||||||
.added('v1')
|
.added('v1')
|
||||||
.stable('v2')
|
.stable('v2')
|
||||||
.updated('v2.6.0', 'Using -1 as a rating is deprecated and will be removed in the next major version.')
|
.updated('v2.6.0', 'Using -1 as a rating is deprecated and will be removed in the next major version.')
|
||||||
|
.updated('v3', 'Using -1 as a rating is no longer valid.')
|
||||||
.getExtensions(),
|
.getExtensions(),
|
||||||
}),
|
}),
|
||||||
ocr: z.string().optional().describe('Filter by OCR text content'),
|
ocr: z.string().optional().describe('Filter by OCR text content'),
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { Kysely, sql } from 'kysely';
|
||||||
|
|
||||||
|
export async function up(db: Kysely<any>): Promise<void> {
|
||||||
|
await sql`UPDATE "asset_exif" SET "rating" = NULL WHERE "rating" = -1;`.execute(db);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(): Promise<void> {
|
||||||
|
// not supported
|
||||||
|
}
|
||||||
@@ -1436,20 +1436,6 @@ describe(MetadataService.name, () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle valid negative rating value', async () => {
|
|
||||||
const asset = AssetFactory.create();
|
|
||||||
mocks.assetJob.getForMetadataExtraction.mockResolvedValue(getForMetadataExtraction(asset));
|
|
||||||
mockReadTags({ Rating: -1 });
|
|
||||||
|
|
||||||
await sut.handleMetadataExtraction({ id: asset.id });
|
|
||||||
expect(mocks.asset.upsertExif).toHaveBeenCalledWith(
|
|
||||||
expect.objectContaining({
|
|
||||||
rating: -1,
|
|
||||||
}),
|
|
||||||
{ lockedPropertiesBehavior: 'skip' },
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should handle livePhotoCID not set', async () => {
|
it('should handle livePhotoCID not set', async () => {
|
||||||
const asset = AssetFactory.create();
|
const asset = AssetFactory.create();
|
||||||
mocks.assetJob.getForMetadataExtraction.mockResolvedValue(getForMetadataExtraction(asset));
|
mocks.assetJob.getForMetadataExtraction.mockResolvedValue(getForMetadataExtraction(asset));
|
||||||
|
|||||||
@@ -304,7 +304,7 @@ export class MetadataService extends BaseService {
|
|||||||
// comments
|
// comments
|
||||||
description: String(exifTags.ImageDescription || exifTags.Description || '').trim(),
|
description: String(exifTags.ImageDescription || exifTags.Description || '').trim(),
|
||||||
profileDescription: exifTags.ProfileDescription || null,
|
profileDescription: exifTags.ProfileDescription || null,
|
||||||
rating: exifTags.Rating === 0 ? null : validateRange(exifTags.Rating, -1, 5),
|
rating: exifTags.Rating === 0 ? null : validateRange(exifTags.Rating, 1, 5),
|
||||||
|
|
||||||
// grouping
|
// grouping
|
||||||
livePhotoCID: (exifTags.ContentIdentifier || exifTags.MediaGroupUUID) ?? null,
|
livePhotoCID: (exifTags.ContentIdentifier || exifTags.MediaGroupUUID) ?? null,
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ describe('duplicate utils', () => {
|
|||||||
model: null,
|
model: null,
|
||||||
latitude: undefined,
|
latitude: undefined,
|
||||||
city: '',
|
city: '',
|
||||||
rating: 0,
|
rating: null,
|
||||||
});
|
});
|
||||||
// fileSizeInByte (1000) + make ('Canon') = 2 truthy values
|
// fileSizeInByte (1000) + make ('Canon') = 2 truthy values
|
||||||
// model (null), latitude (undefined), city (''), rating (0) are all falsy
|
// model (null), latitude (undefined), city (''), rating (0) are all falsy
|
||||||
|
|||||||
Reference in New Issue
Block a user