Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: orientation #12792

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion e2e/src/api/specs/asset.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1052,7 +1052,7 @@
dateTimeOriginal: '2010-07-20T17:27:12.000Z',
latitude: null,
longitude: null,
orientation: '1',
orientation: 1,
},
},
},
Expand Down Expand Up @@ -1172,7 +1172,7 @@
const asset = await utils.getAssetInfo(admin.accessToken, id);

expect(asset.exifInfo).toBeDefined();
expect(asset.exifInfo).toMatchObject(expected.exifInfo);

Check failure on line 1175 in e2e/src/api/specs/asset.e2e-spec.ts

View workflow job for this annotation

GitHub Actions / End-to-End Tests (Server & CLI)

src/api/specs/asset.e2e-spec.ts > /asset > POST /assets > should upload and generate a thumbnail for different file types

AssertionError: expected { make: 'NIKON CORPORATION', …(21) } to match object { make: 'NIKON CORPORATION', …(11) } (10 matching properties omitted from actual) - Expected + Received Object { "dateTimeOriginal": "2016-09-22T22:10:29.060Z", "exposureTime": "1/400", "fNumber": 11, "fileSizeInByte": 15856335, "focalLength": 85, "iso": 200, "latitude": null, "longitude": null, "make": "NIKON CORPORATION", "model": "NIKON D700", - "orientation": "1", + "orientation": 1, "timeZone": "UTC-5", } ❯ src/api/specs/asset.e2e-spec.ts:1175:32
expect(asset).toMatchObject(expected);
}
});
Expand Down
98 changes: 96 additions & 2 deletions mobile/openapi/lib/model/exif_response_dto.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 11 additions & 1 deletion open-api/immich-openapi-specs.json
Original file line number Diff line number Diff line change
Expand Up @@ -9074,8 +9074,18 @@
},
"orientation": {
"default": null,
"enum": [
1,
2,
8,
7,
3,
4,
6,
5
],
"nullable": true,
"type": "string"
"type": "number"
},
"projectionType": {
"default": null,
Expand Down
12 changes: 11 additions & 1 deletion open-api/typescript-sdk/src/fetch-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ export type ExifResponseDto = {
make?: string | null;
model?: string | null;
modifyDate?: string | null;
orientation?: string | null;
orientation?: Orientation | null;
projectionType?: string | null;
rating?: number | null;
state?: string | null;
Expand Down Expand Up @@ -3266,6 +3266,16 @@ export enum AlbumUserRole {
Editor = "editor",
Viewer = "viewer"
}
export enum Orientation {
$1 = 1,
$2 = 2,
$8 = 8,
$7 = 7,
$3 = 3,
$4 = 4,
$6 = 6,
$5 = 5
}
export enum SourceType {
MachineLearning = "machine-learning",
Exif = "exif"
Expand Down
12 changes: 12 additions & 0 deletions open-api/typescript-sdk/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { defaults } from './fetch-client.js';
export { Orientation as LameGeneratedOrientation } from './fetch-client.js';

export * from './fetch-client.js';
export * from './fetch-errors.js';
Expand All @@ -8,6 +9,17 @@ export interface InitOptions {
apiKey: string;
}

export enum Orientation {
Rotate0 = 1,
Rotate0Mirrored = 2,
Rotate90 = 8,
Rotate90Mirrored = 7,
Rotate180 = 3,
Rotate180Mirrored = 4,
Rotate270 = 6,
Rotate270Mirrored = 5,
}

export const init = ({ baseUrl, apiKey }: InitOptions) => {
setBaseUrl(baseUrl);
setApiKey(apiKey);
Expand Down
5 changes: 4 additions & 1 deletion server/src/dtos/exif.dto.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { ApiProperty } from '@nestjs/swagger';
import { ExifEntity } from 'src/entities/exif.entity';
import { Orientation } from 'src/enum';

export class ExifResponseDto {
make?: string | null = null;
Expand All @@ -9,7 +10,9 @@ export class ExifResponseDto {

@ApiProperty({ type: 'integer', format: 'int64' })
fileSizeInByte?: number | null = null;
orientation?: string | null = null;

@ApiProperty({ enum: Orientation })
orientation?: Orientation | null = null;
dateTimeOriginal?: Date | null = null;
modifyDate?: Date | null = null;
timeZone?: string | null = null;
Expand Down
5 changes: 3 additions & 2 deletions server/src/entities/exif.entity.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { AssetEntity } from 'src/entities/asset.entity';
import { Orientation } from 'src/enum';
import { Index, JoinColumn, OneToOne, PrimaryColumn } from 'typeorm';
import { Column } from 'typeorm/decorator/columns/Column.js';
import { Entity } from 'typeorm/decorator/entity/Entity.js';
Expand All @@ -25,8 +26,8 @@ export class ExifEntity {
@Column({ type: 'bigint', nullable: true })
fileSizeInByte!: number | null;

@Column({ type: 'varchar', nullable: true })
orientation!: string | null;
@Column({ type: 'enum', enum: Orientation, nullable: true })
Copy link
Contributor

@mertalev mertalev Oct 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For this case, the keys are basically already enum values. It'd be nice to make this a "char" (not char) and use a check constraint that it's between 1-8. It's a 1 byte integer instead of a 4 byte float and doesn't have the string vs number ambiguity that an enum would have.

orientation!: Orientation | null;

@Column({ type: 'timestamptz', nullable: true })
dateTimeOriginal!: Date | null;
Expand Down
11 changes: 11 additions & 0 deletions server/src/enum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,3 +198,14 @@ export enum ManualJobName {
TAG_CLEANUP = 'tag-cleanup',
USER_CLEANUP = 'user-cleanup',
}

export enum Orientation {
Rotate0 = 1,
Rotate0Mirrored = 2,
Rotate90 = 8,
Rotate90Mirrored = 7,
Rotate180 = 3,
Rotate180Mirrored = 4,
Rotate270 = 6,
Rotate270Mirrored = 5,
}
31 changes: 31 additions & 0 deletions server/src/migrations/1726754669860-AddOrientationEnum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { MigrationInterface, QueryRunner } from "typeorm";

export class AddOrientationEnum1726754669860 implements MigrationInterface {
name = 'AddOrientationEnum1726754669860'

public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`CREATE TYPE "exif_orientation_enum" AS ENUM('1', '2', '3', '4', '5', '6', '7', '8')`)
await queryRunner.query(`
UPDATE "exif" SET "orientation" = CASE
WHEN "orientation" = '0' THEN '1'
WHEN "orientation" = '1' THEN '1'
WHEN "orientation" = '2' THEN '2'
WHEN "orientation" = '3' THEN '3'
WHEN "orientation" = '4' THEN '4'
WHEN "orientation" = '5' THEN '5'
WHEN "orientation" = '6' THEN '6'
WHEN "orientation" = '7' THEN '7'
WHEN "orientation" = '8' THEN '8'
WHEN "orientation" = '-90' THEN '6'
WHEN "orientation" = '90' THEN '8'
WHEN "orientation" = '180' THEN '3'
ELSE NULL
END`);
await queryRunner.query(`ALTER TABLE "exif" ALTER COLUMN "orientation" TYPE "exif_orientation_enum" USING "orientation"::"exif_orientation_enum"`);
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "exif" ALTER COLUMN "orientation" TYPE character varying USING "orientation"::text`);
await queryRunner.query(`DROP TYPE "exif_orientation_enum"`);
}
}
12 changes: 5 additions & 7 deletions server/src/services/metadata.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { randomBytes } from 'node:crypto';
import { Stats } from 'node:fs';
import { constants } from 'node:fs/promises';
import { ExifEntity } from 'src/entities/exif.entity';
import { AssetType, SourceType } from 'src/enum';
import { AssetType, Orientation, SourceType } from 'src/enum';
import { IAlbumRepository } from 'src/interfaces/album.interface';
import { IAssetRepository, WithoutProperty } from 'src/interfaces/asset.interface';
import { ICryptoRepository } from 'src/interfaces/crypto.interface';
Expand All @@ -20,7 +20,7 @@ import { IStorageRepository } from 'src/interfaces/storage.interface';
import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface';
import { ITagRepository } from 'src/interfaces/tag.interface';
import { IUserRepository } from 'src/interfaces/user.interface';
import { MetadataService, Orientation } from 'src/services/metadata.service';
import { MetadataService } from 'src/services/metadata.service';
import { assetStub } from 'test/fixtures/asset.stub';
import { fileStub } from 'test/fixtures/file.stub';
import { probeStub } from 'test/fixtures/media.stub';
Expand Down Expand Up @@ -537,9 +537,7 @@ describe(MetadataService.name, () => {
await sut.handleMetadataExtraction({ id: assetStub.video.id });

expect(assetMock.getByIds).toHaveBeenCalledWith([assetStub.video.id]);
expect(assetMock.upsertExif).toHaveBeenCalledWith(
expect.objectContaining({ orientation: Orientation.Rotate270CW.toString() }),
);
expect(assetMock.upsertExif).toHaveBeenCalledWith(expect.objectContaining({ orientation: Orientation.Rotate90 }));
});

it('should extract the MotionPhotoVideo tag from Samsung HEIC motion photos', async () => {
Expand Down Expand Up @@ -786,7 +784,7 @@ describe(MetadataService.name, () => {
Make: 'test-factory',
Model: "'mockel'",
ModifyDate: ExifDateTime.fromISO(dateForTest.toISOString()),
Orientation: 0,
Orientation: 1,
ProfileDescription: 'extensive description',
ProjectionType: 'equirectangular',
tz: 'UTC-11:30',
Expand Down Expand Up @@ -819,7 +817,7 @@ describe(MetadataService.name, () => {
make: tags.Make,
model: tags.Model,
modifyDate: expect.any(Date),
orientation: tags.Orientation?.toString(),
orientation: 1,
profileDescription: tags.ProfileDescription,
projectionType: 'EQUIRECTANGULAR',
timeZone: tags.tz,
Expand Down
Loading
Loading