Browse Source

save value

martabal 1 year ago
parent
commit
0309bba7dd

+ 12 - 0
cli/src/api/open-api/api.ts

@@ -483,6 +483,12 @@ export interface AssetBulkUpdateDto {
      * @memberof AssetBulkUpdateDto
      * @memberof AssetBulkUpdateDto
      */
      */
     'longitude'?: number;
     'longitude'?: number;
+    /**
+     * 
+     * @type {number}
+     * @memberof AssetBulkUpdateDto
+     */
+    'orientation'?: number;
     /**
     /**
      * 
      * 
      * @type {boolean}
      * @type {boolean}
@@ -4191,6 +4197,12 @@ export interface UpdateAssetDto {
      * @memberof UpdateAssetDto
      * @memberof UpdateAssetDto
      */
      */
     'longitude'?: number;
     'longitude'?: number;
+    /**
+     * 
+     * @type {number}
+     * @memberof UpdateAssetDto
+     */
+    'orientation'?: number;
 }
 }
 /**
 /**
  * 
  * 

+ 1 - 0
mobile/openapi/doc/AssetBulkUpdateDto.md

@@ -14,6 +14,7 @@ Name | Type | Description | Notes
 **isFavorite** | **bool** |  | [optional] 
 **isFavorite** | **bool** |  | [optional] 
 **latitude** | **num** |  | [optional] 
 **latitude** | **num** |  | [optional] 
 **longitude** | **num** |  | [optional] 
 **longitude** | **num** |  | [optional] 
+**orientation** | **num** |  | [optional] 
 **removeParent** | **bool** |  | [optional] 
 **removeParent** | **bool** |  | [optional] 
 **stackParentId** | **String** |  | [optional] 
 **stackParentId** | **String** |  | [optional] 
 
 

+ 1 - 0
mobile/openapi/doc/UpdateAssetDto.md

@@ -14,6 +14,7 @@ Name | Type | Description | Notes
 **isFavorite** | **bool** |  | [optional] 
 **isFavorite** | **bool** |  | [optional] 
 **latitude** | **num** |  | [optional] 
 **latitude** | **num** |  | [optional] 
 **longitude** | **num** |  | [optional] 
 **longitude** | **num** |  | [optional] 
+**orientation** | **num** |  | [optional] 
 
 
 [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
 [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
 
 

+ 20 - 1
mobile/openapi/lib/model/asset_bulk_update_dto.dart

@@ -19,6 +19,7 @@ class AssetBulkUpdateDto {
     this.isFavorite,
     this.isFavorite,
     this.latitude,
     this.latitude,
     this.longitude,
     this.longitude,
+    this.orientation,
     this.removeParent,
     this.removeParent,
     this.stackParentId,
     this.stackParentId,
   });
   });
@@ -65,6 +66,14 @@ class AssetBulkUpdateDto {
   ///
   ///
   num? longitude;
   num? longitude;
 
 
+  ///
+  /// Please note: This property should have been non-nullable! Since the specification file
+  /// does not include a default value (using the "default:" property), however, the generated
+  /// source code must fall back to having a nullable type.
+  /// Consider adding a "default:" property in the specification file to hide this note.
+  ///
+  num? orientation;
+
   ///
   ///
   /// Please note: This property should have been non-nullable! Since the specification file
   /// Please note: This property should have been non-nullable! Since the specification file
   /// does not include a default value (using the "default:" property), however, the generated
   /// does not include a default value (using the "default:" property), however, the generated
@@ -89,6 +98,7 @@ class AssetBulkUpdateDto {
      other.isFavorite == isFavorite &&
      other.isFavorite == isFavorite &&
      other.latitude == latitude &&
      other.latitude == latitude &&
      other.longitude == longitude &&
      other.longitude == longitude &&
+     other.orientation == orientation &&
      other.removeParent == removeParent &&
      other.removeParent == removeParent &&
      other.stackParentId == stackParentId;
      other.stackParentId == stackParentId;
 
 
@@ -101,11 +111,12 @@ class AssetBulkUpdateDto {
     (isFavorite == null ? 0 : isFavorite!.hashCode) +
     (isFavorite == null ? 0 : isFavorite!.hashCode) +
     (latitude == null ? 0 : latitude!.hashCode) +
     (latitude == null ? 0 : latitude!.hashCode) +
     (longitude == null ? 0 : longitude!.hashCode) +
     (longitude == null ? 0 : longitude!.hashCode) +
+    (orientation == null ? 0 : orientation!.hashCode) +
     (removeParent == null ? 0 : removeParent!.hashCode) +
     (removeParent == null ? 0 : removeParent!.hashCode) +
     (stackParentId == null ? 0 : stackParentId!.hashCode);
     (stackParentId == null ? 0 : stackParentId!.hashCode);
 
 
   @override
   @override
-  String toString() => 'AssetBulkUpdateDto[dateTimeOriginal=$dateTimeOriginal, ids=$ids, isArchived=$isArchived, isFavorite=$isFavorite, latitude=$latitude, longitude=$longitude, removeParent=$removeParent, stackParentId=$stackParentId]';
+  String toString() => 'AssetBulkUpdateDto[dateTimeOriginal=$dateTimeOriginal, ids=$ids, isArchived=$isArchived, isFavorite=$isFavorite, latitude=$latitude, longitude=$longitude, orientation=$orientation, removeParent=$removeParent, stackParentId=$stackParentId]';
 
 
   Map<String, dynamic> toJson() {
   Map<String, dynamic> toJson() {
     final json = <String, dynamic>{};
     final json = <String, dynamic>{};
@@ -135,6 +146,11 @@ class AssetBulkUpdateDto {
     } else {
     } else {
     //  json[r'longitude'] = null;
     //  json[r'longitude'] = null;
     }
     }
+    if (this.orientation != null) {
+      json[r'orientation'] = this.orientation;
+    } else {
+    //  json[r'orientation'] = null;
+    }
     if (this.removeParent != null) {
     if (this.removeParent != null) {
       json[r'removeParent'] = this.removeParent;
       json[r'removeParent'] = this.removeParent;
     } else {
     } else {
@@ -168,6 +184,9 @@ class AssetBulkUpdateDto {
         longitude: json[r'longitude'] == null
         longitude: json[r'longitude'] == null
             ? null
             ? null
             : num.parse(json[r'longitude'].toString()),
             : num.parse(json[r'longitude'].toString()),
+        orientation: json[r'orientation'] == null
+            ? null
+            : num.parse(json[r'orientation'].toString()),
         removeParent: mapValueOfType<bool>(json, r'removeParent'),
         removeParent: mapValueOfType<bool>(json, r'removeParent'),
         stackParentId: mapValueOfType<String>(json, r'stackParentId'),
         stackParentId: mapValueOfType<String>(json, r'stackParentId'),
       );
       );

+ 22 - 3
mobile/openapi/lib/model/update_asset_dto.dart

@@ -19,6 +19,7 @@ class UpdateAssetDto {
     this.isFavorite,
     this.isFavorite,
     this.latitude,
     this.latitude,
     this.longitude,
     this.longitude,
+    this.orientation,
   });
   });
 
 
   ///
   ///
@@ -69,6 +70,14 @@ class UpdateAssetDto {
   ///
   ///
   num? longitude;
   num? longitude;
 
 
+  ///
+  /// Please note: This property should have been non-nullable! Since the specification file
+  /// does not include a default value (using the "default:" property), however, the generated
+  /// source code must fall back to having a nullable type.
+  /// Consider adding a "default:" property in the specification file to hide this note.
+  ///
+  num? orientation;
+
   @override
   @override
   bool operator ==(Object other) => identical(this, other) || other is UpdateAssetDto &&
   bool operator ==(Object other) => identical(this, other) || other is UpdateAssetDto &&
      other.dateTimeOriginal == dateTimeOriginal &&
      other.dateTimeOriginal == dateTimeOriginal &&
@@ -76,7 +85,8 @@ class UpdateAssetDto {
      other.isArchived == isArchived &&
      other.isArchived == isArchived &&
      other.isFavorite == isFavorite &&
      other.isFavorite == isFavorite &&
      other.latitude == latitude &&
      other.latitude == latitude &&
-     other.longitude == longitude;
+     other.longitude == longitude &&
+     other.orientation == orientation;
 
 
   @override
   @override
   int get hashCode =>
   int get hashCode =>
@@ -86,10 +96,11 @@ class UpdateAssetDto {
     (isArchived == null ? 0 : isArchived!.hashCode) +
     (isArchived == null ? 0 : isArchived!.hashCode) +
     (isFavorite == null ? 0 : isFavorite!.hashCode) +
     (isFavorite == null ? 0 : isFavorite!.hashCode) +
     (latitude == null ? 0 : latitude!.hashCode) +
     (latitude == null ? 0 : latitude!.hashCode) +
-    (longitude == null ? 0 : longitude!.hashCode);
+    (longitude == null ? 0 : longitude!.hashCode) +
+    (orientation == null ? 0 : orientation!.hashCode);
 
 
   @override
   @override
-  String toString() => 'UpdateAssetDto[dateTimeOriginal=$dateTimeOriginal, description=$description, isArchived=$isArchived, isFavorite=$isFavorite, latitude=$latitude, longitude=$longitude]';
+  String toString() => 'UpdateAssetDto[dateTimeOriginal=$dateTimeOriginal, description=$description, isArchived=$isArchived, isFavorite=$isFavorite, latitude=$latitude, longitude=$longitude, orientation=$orientation]';
 
 
   Map<String, dynamic> toJson() {
   Map<String, dynamic> toJson() {
     final json = <String, dynamic>{};
     final json = <String, dynamic>{};
@@ -123,6 +134,11 @@ class UpdateAssetDto {
     } else {
     } else {
     //  json[r'longitude'] = null;
     //  json[r'longitude'] = null;
     }
     }
+    if (this.orientation != null) {
+      json[r'orientation'] = this.orientation;
+    } else {
+    //  json[r'orientation'] = null;
+    }
     return json;
     return json;
   }
   }
 
 
@@ -144,6 +160,9 @@ class UpdateAssetDto {
         longitude: json[r'longitude'] == null
         longitude: json[r'longitude'] == null
             ? null
             ? null
             : num.parse(json[r'longitude'].toString()),
             : num.parse(json[r'longitude'].toString()),
+        orientation: json[r'orientation'] == null
+            ? null
+            : num.parse(json[r'orientation'].toString()),
       );
       );
     }
     }
     return null;
     return null;

+ 5 - 0
mobile/openapi/test/asset_bulk_update_dto_test.dart

@@ -46,6 +46,11 @@ void main() {
       // TODO
       // TODO
     });
     });
 
 
+    // num orientation
+    test('to test the property `orientation`', () async {
+      // TODO
+    });
+
     // bool removeParent
     // bool removeParent
     test('to test the property `removeParent`', () async {
     test('to test the property `removeParent`', () async {
       // TODO
       // TODO

+ 5 - 0
mobile/openapi/test/update_asset_dto_test.dart

@@ -46,6 +46,11 @@ void main() {
       // TODO
       // TODO
     });
     });
 
 
+    // num orientation
+    test('to test the property `orientation`', () async {
+      // TODO
+    });
+
 
 
   });
   });
 
 

+ 6 - 0
server/immich-openapi-specs.json

@@ -6471,6 +6471,9 @@
           "longitude": {
           "longitude": {
             "type": "number"
             "type": "number"
           },
           },
+          "orientation": {
+            "type": "number"
+          },
           "removeParent": {
           "removeParent": {
             "type": "boolean"
             "type": "boolean"
           },
           },
@@ -9369,6 +9372,9 @@
           },
           },
           "longitude": {
           "longitude": {
             "type": "number"
             "type": "number"
+          },
+          "orientation": {
+            "type": "number"
           }
           }
         },
         },
         "type": "object"
         "type": "object"

+ 7 - 6
server/src/domain/asset/asset.service.ts

@@ -393,8 +393,8 @@ export class AssetService {
   async update(authUser: AuthUserDto, id: string, dto: UpdateAssetDto): Promise<AssetResponseDto> {
   async update(authUser: AuthUserDto, id: string, dto: UpdateAssetDto): Promise<AssetResponseDto> {
     await this.access.requirePermission(authUser, Permission.ASSET_UPDATE, id);
     await this.access.requirePermission(authUser, Permission.ASSET_UPDATE, id);
 
 
-    const { description, dateTimeOriginal, latitude, longitude, ...rest } = dto;
-    await this.updateMetadata({ id, description, dateTimeOriginal, latitude, longitude });
+    const { description, dateTimeOriginal, latitude, longitude, orientation, ...rest } = dto;
+    await this.updateMetadata({ id, description, dateTimeOriginal, latitude, longitude, orientation });
 
 
     const asset = await this.assetRepository.save({ id, ...rest });
     const asset = await this.assetRepository.save({ id, ...rest });
     await this.jobRepository.queue({ name: JobName.SEARCH_INDEX_ASSET, data: { ids: [id] } });
     await this.jobRepository.queue({ name: JobName.SEARCH_INDEX_ASSET, data: { ids: [id] } });
@@ -402,7 +402,7 @@ export class AssetService {
   }
   }
 
 
   async updateAll(authUser: AuthUserDto, dto: AssetBulkUpdateDto): Promise<void> {
   async updateAll(authUser: AuthUserDto, dto: AssetBulkUpdateDto): Promise<void> {
-    const { ids, removeParent, dateTimeOriginal, latitude, longitude, ...options } = dto;
+    const { ids, removeParent, dateTimeOriginal, latitude, longitude, orientation, ...options } = dto;
     await this.access.requirePermission(authUser, Permission.ASSET_UPDATE, ids);
     await this.access.requirePermission(authUser, Permission.ASSET_UPDATE, ids);
 
 
     if (removeParent) {
     if (removeParent) {
@@ -423,7 +423,7 @@ export class AssetService {
     }
     }
 
 
     for (const id of ids) {
     for (const id of ids) {
-      await this.updateMetadata({ id, dateTimeOriginal, latitude, longitude });
+      await this.updateMetadata({ id, dateTimeOriginal, latitude, longitude, orientation });
     }
     }
 
 
     await this.jobRepository.queue({ name: JobName.SEARCH_INDEX_ASSET, data: { ids } });
     await this.jobRepository.queue({ name: JobName.SEARCH_INDEX_ASSET, data: { ids } });
@@ -591,8 +591,9 @@ export class AssetService {
   }
   }
 
 
   private async updateMetadata(dto: ISidecarWriteJob) {
   private async updateMetadata(dto: ISidecarWriteJob) {
-    const { id, description, dateTimeOriginal, latitude, longitude } = dto;
-    const writes = _.omitBy({ description, dateTimeOriginal, latitude, longitude }, _.isUndefined);
+    const { id, description, dateTimeOriginal, latitude, longitude, orientation } = dto;
+    const writes = _.omitBy({ description, dateTimeOriginal, latitude, longitude, orientation }, _.isUndefined);
+    console.log('updatemetadta', orientation);
     if (Object.keys(writes).length > 0) {
     if (Object.keys(writes).length > 0) {
       await this.assetRepository.upsertExif({ assetId: id, ...writes });
       await this.assetRepository.upsertExif({ assetId: id, ...writes });
       await this.jobRepository.queue({ name: JobName.SIDECAR_WRITE, data: { id, ...writes } });
       await this.jobRepository.queue({ name: JobName.SIDECAR_WRITE, data: { id, ...writes } });

+ 10 - 0
server/src/domain/asset/dto/asset.dto.ts

@@ -202,6 +202,11 @@ export class AssetBulkUpdateDto extends BulkIdsDto {
   @IsLongitude()
   @IsLongitude()
   @IsNotEmpty()
   @IsNotEmpty()
   longitude?: number;
   longitude?: number;
+
+  @Optional()
+  @IsInt()
+  @Type(() => Number)
+  orientation?: number;
 }
 }
 
 
 export class UpdateAssetDto {
 export class UpdateAssetDto {
@@ -230,6 +235,11 @@ export class UpdateAssetDto {
   @IsLongitude()
   @IsLongitude()
   @IsNotEmpty()
   @IsNotEmpty()
   longitude?: number;
   longitude?: number;
+
+  @Optional()
+  @IsInt()
+  @Type(() => Number)
+  orientation?: number;
 }
 }
 
 
 export class RandomAssetsDto {
 export class RandomAssetsDto {

+ 1 - 0
server/src/domain/job/job.interface.ts

@@ -39,4 +39,5 @@ export interface ISidecarWriteJob extends IEntityJob {
   dateTimeOriginal?: string;
   dateTimeOriginal?: string;
   latitude?: number;
   latitude?: number;
   longitude?: number;
   longitude?: number;
+  orientation?: number;
 }
 }

+ 2 - 1
server/src/domain/metadata/metadata.service.ts

@@ -245,7 +245,7 @@ export class MetadataService {
   }
   }
 
 
   async handleSidecarWrite(job: ISidecarWriteJob) {
   async handleSidecarWrite(job: ISidecarWriteJob) {
-    const { id, description, dateTimeOriginal, latitude, longitude } = job;
+    const { id, description, dateTimeOriginal, latitude, longitude, orientation } = job;
     const [asset] = await this.assetRepository.getByIds([id]);
     const [asset] = await this.assetRepository.getByIds([id]);
     if (!asset) {
     if (!asset) {
       return false;
       return false;
@@ -258,6 +258,7 @@ export class MetadataService {
         CreationDate: dateTimeOriginal,
         CreationDate: dateTimeOriginal,
         GPSLatitude: latitude,
         GPSLatitude: latitude,
         GPSLongitude: longitude,
         GPSLongitude: longitude,
+        Orientation: orientation,
       },
       },
       _.isUndefined,
       _.isUndefined,
     );
     );

+ 1 - 0
server/src/domain/repositories/metadata.repository.ts

@@ -27,6 +27,7 @@ export interface ImmichTags extends Omit<Tags, 'FocalLength' | 'Duration'> {
   ImagePixelDepth?: string;
   ImagePixelDepth?: string;
   FocalLength?: number;
   FocalLength?: number;
   Duration?: number | ExifDuration;
   Duration?: number | ExifDuration;
+  Orientation?: number;
 }
 }
 
 
 export interface IMetadataRepository {
 export interface IMetadataRepository {

+ 12 - 0
web/src/api/open-api/api.ts

@@ -483,6 +483,12 @@ export interface AssetBulkUpdateDto {
      * @memberof AssetBulkUpdateDto
      * @memberof AssetBulkUpdateDto
      */
      */
     'longitude'?: number;
     'longitude'?: number;
+    /**
+     * 
+     * @type {number}
+     * @memberof AssetBulkUpdateDto
+     */
+    'orientation'?: number;
     /**
     /**
      * 
      * 
      * @type {boolean}
      * @type {boolean}
@@ -4191,6 +4197,12 @@ export interface UpdateAssetDto {
      * @memberof UpdateAssetDto
      * @memberof UpdateAssetDto
      */
      */
     'longitude'?: number;
     'longitude'?: number;
+    /**
+     * 
+     * @type {number}
+     * @memberof UpdateAssetDto
+     */
+    'orientation'?: number;
 }
 }
 /**
 /**
  * 
  * 

+ 1 - 1
web/src/lib/components/asset-viewer/asset-viewer.svelte

@@ -55,7 +55,7 @@
   export let album: AlbumResponseDto | null = null;
   export let album: AlbumResponseDto | null = null;
 
 
   let reactions: ActivityResponseDto[] = [];
   let reactions: ActivityResponseDto[] = [];
-  let rotatePhotoviewer: () => void;
+  let rotatePhotoviewer: () => Promise<void>;
   const { setAssetId } = assetViewingStore;
   const { setAssetId } = assetViewingStore;
   const {
   const {
     restartProgress: restartSlideshowProgress,
     restartProgress: restartSlideshowProgress,

+ 55 - 2
web/src/lib/components/asset-viewer/photo-viewer.svelte

@@ -6,13 +6,56 @@
   import { notificationController, NotificationType } from '../shared-components/notification/notification';
   import { notificationController, NotificationType } from '../shared-components/notification/notification';
   import { useZoomImageWheel } from '@zoom-image/svelte';
   import { useZoomImageWheel } from '@zoom-image/svelte';
   import { photoZoomState } from '$lib/stores/zoom-image.store';
   import { photoZoomState } from '$lib/stores/zoom-image.store';
-  import { isWebCompatibleImage } from '$lib/utils/asset-utils';
+  import { getAssetRatio, isWebCompatibleImage } from '$lib/utils/asset-utils';
   import { shouldIgnoreShortcut } from '$lib/utils/shortcut';
   import { shouldIgnoreShortcut } from '$lib/utils/shortcut';
+  import { handleError } from '$lib/utils/handle-error';
 
 
   export let asset: AssetResponseDto;
   export let asset: AssetResponseDto;
   export let element: HTMLDivElement | undefined = undefined;
   export let element: HTMLDivElement | undefined = undefined;
   export let haveFadeTransition = true;
   export let haveFadeTransition = true;
-  export const rotate = () => setZoomImageWheelState({ currentRotation: $zoomImageWheelState.currentRotation - 90 });
+
+  const getRotation = (value: string): number => {
+    switch (value) {
+      case '1':
+        return 0;
+      case '3':
+        return 180;
+      case '6':
+        return 90;
+      case '8':
+        return 270;
+      default:
+        return 0;
+    }
+  };
+
+  const getRotationString = (rotation: number): number => {
+    switch (rotation % 360) {
+      case 0:
+        return 1;
+      case 90:
+        return 6;
+      case 180:
+        return 3;
+      case 270:
+        return 8;
+      default:
+        return 1;
+    }
+  };
+
+  export const rotate = async () => {
+    setZoomImageWheelState({ currentRotation: $zoomImageWheelState.currentRotation - 90 });
+    console.log(getRotationString($zoomImageWheelState.currentRotation));
+    try {
+      await api.assetApi.updateAsset({
+        id: asset.id,
+        updateAssetDto: { orientation: getRotationString($zoomImageWheelState.currentRotation) },
+      });
+    } catch (error) {
+      handleError(error, 'Unable to change orientation');
+    }
+  };
 
 
   let imgElement: HTMLDivElement;
   let imgElement: HTMLDivElement;
   let assetData: string;
   let assetData: string;
@@ -115,6 +158,16 @@
       maxZoom: 10,
       maxZoom: 10,
       wheelZoomRatio: 0.2,
       wheelZoomRatio: 0.2,
     });
     });
+    if (asset.exifInfo?.orientation) {
+      const { width, height } = getAssetRatio(asset);
+      if (width > height && parseInt(asset.exifInfo?.orientation) != 1) {
+        setZoomImageWheelState({ currentRotation: getRotation(asset.exifInfo?.orientation) });
+      }
+
+      if (width < height && parseInt(asset.exifInfo?.orientation) != 6) {
+        setZoomImageWheelState({ currentRotation: getRotation(asset.exifInfo?.orientation) });
+      }
+    }
   }
   }
 </script>
 </script>