You've already forked immich
							
							
				mirror of
				https://github.com/immich-app/immich.git
				synced 2025-10-31 00:18:28 +02:00 
			
		
		
		
	refactor(server): download file (#1512)
* refactor(server): download file * chore: generate open-api and remove unused refs * chore(server): tests * chore: remove unused code
This commit is contained in:
		| @@ -26,14 +26,10 @@ class ImageViewerService { | ||||
|       if (asset.type == AssetTypeEnum.IMAGE && asset.livePhotoVideoId != null) { | ||||
|         var imageResponse = await _apiService.assetApi.downloadFileWithHttpInfo( | ||||
|           asset.id, | ||||
|           isThumb: false, | ||||
|           isWeb: false, | ||||
|         ); | ||||
|  | ||||
|         var motionReponse = await _apiService.assetApi.downloadFileWithHttpInfo( | ||||
|           asset.livePhotoVideoId!, | ||||
|           isThumb: false, | ||||
|           isWeb: false, | ||||
|         ); | ||||
|  | ||||
|         final AssetEntity? entity; | ||||
| @@ -54,8 +50,6 @@ class ImageViewerService { | ||||
|       } else { | ||||
|         var res = await _apiService.assetApi.downloadFileWithHttpInfo( | ||||
|           asset.id, | ||||
|           isThumb: false, | ||||
|           isWeb: false, | ||||
|         ); | ||||
|  | ||||
|         final AssetEntity? entity; | ||||
|   | ||||
| @@ -29,8 +29,6 @@ class ShareService { | ||||
|         final tempFile = await File('${tempDir.path}/$fileName').create(); | ||||
|         final res = await _apiService.assetApi.downloadFileWithHttpInfo( | ||||
|           asset.remote!.id, | ||||
|           isThumb: false, | ||||
|           isWeb: false, | ||||
|         ); | ||||
|         tempFile.writeAsBytesSync(res.bodyBytes); | ||||
|         return XFile(tempFile.path); | ||||
|   | ||||
							
								
								
									
										2
									
								
								mobile/openapi/README.md
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2
									
								
								mobile/openapi/README.md
									
									
									
										generated
									
									
									
								
							| @@ -3,7 +3,7 @@ Immich API | ||||
| 
 | ||||
| This Dart package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project: | ||||
| 
 | ||||
| - API version: 1.43.0 | ||||
| - API version: 1.43.1 | ||||
| - Build package: org.openapitools.codegen.languages.DartClientCodegen | ||||
| 
 | ||||
| ## Requirements | ||||
|   | ||||
							
								
								
									
										8
									
								
								mobile/openapi/doc/AssetApi.md
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										8
									
								
								mobile/openapi/doc/AssetApi.md
									
									
									
										generated
									
									
									
								
							| @@ -230,7 +230,7 @@ Name | Type | Description  | Notes | ||||
| [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) | ||||
| 
 | ||||
| # **downloadFile** | ||||
| > Object downloadFile(assetId, isThumb, isWeb) | ||||
| > Object downloadFile(assetId) | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| @@ -248,11 +248,9 @@ import 'package:openapi/api.dart'; | ||||
| 
 | ||||
| final api_instance = AssetApi(); | ||||
| final assetId = assetId_example; // String |  | ||||
| final isThumb = true; // bool |  | ||||
| final isWeb = true; // bool |  | ||||
| 
 | ||||
| try { | ||||
|     final result = api_instance.downloadFile(assetId, isThumb, isWeb); | ||||
|     final result = api_instance.downloadFile(assetId); | ||||
|     print(result); | ||||
| } catch (e) { | ||||
|     print('Exception when calling AssetApi->downloadFile: $e\n'); | ||||
| @@ -264,8 +262,6 @@ try { | ||||
| Name | Type | Description  | Notes | ||||
| ------------- | ------------- | ------------- | ------------- | ||||
|  **assetId** | **String**|  |  | ||||
|  **isThumb** | **bool**|  | [optional]  | ||||
|  **isWeb** | **bool**|  | [optional]  | ||||
| 
 | ||||
| ### Return type | ||||
| 
 | ||||
|   | ||||
							
								
								
									
										21
									
								
								mobile/openapi/lib/api/asset_api.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										21
									
								
								mobile/openapi/lib/api/asset_api.dart
									
									
									
										generated
									
									
									
								
							| @@ -234,11 +234,7 @@ class AssetApi { | ||||
|   /// Parameters: | ||||
|   /// | ||||
|   /// * [String] assetId (required): | ||||
|   /// | ||||
|   /// * [bool] isThumb: | ||||
|   /// | ||||
|   /// * [bool] isWeb: | ||||
|   Future<Response> downloadFileWithHttpInfo(String assetId, { bool? isThumb, bool? isWeb, }) async { | ||||
|   Future<Response> downloadFileWithHttpInfo(String assetId,) async { | ||||
|     // ignore: prefer_const_declarations | ||||
|     final path = r'/asset/download/{assetId}' | ||||
|       .replaceAll('{assetId}', assetId); | ||||
| @@ -250,13 +246,6 @@ class AssetApi { | ||||
|     final headerParams = <String, String>{}; | ||||
|     final formParams = <String, String>{}; | ||||
| 
 | ||||
|     if (isThumb != null) { | ||||
|       queryParams.addAll(_queryParams('', 'isThumb', isThumb)); | ||||
|     } | ||||
|     if (isWeb != null) { | ||||
|       queryParams.addAll(_queryParams('', 'isWeb', isWeb)); | ||||
|     } | ||||
| 
 | ||||
|     const contentTypes = <String>[]; | ||||
| 
 | ||||
| 
 | ||||
| @@ -276,12 +265,8 @@ class AssetApi { | ||||
|   /// Parameters: | ||||
|   /// | ||||
|   /// * [String] assetId (required): | ||||
|   /// | ||||
|   /// * [bool] isThumb: | ||||
|   /// | ||||
|   /// * [bool] isWeb: | ||||
|   Future<Object?> downloadFile(String assetId, { bool? isThumb, bool? isWeb, }) async { | ||||
|     final response = await downloadFileWithHttpInfo(assetId,  isThumb: isThumb, isWeb: isWeb, ); | ||||
|   Future<Object?> downloadFile(String assetId,) async { | ||||
|     final response = await downloadFileWithHttpInfo(assetId,); | ||||
|     if (response.statusCode >= HttpStatus.badRequest) { | ||||
|       throw ApiException(response.statusCode, await _decodeBodyBytes(response)); | ||||
|     } | ||||
|   | ||||
							
								
								
									
										94
									
								
								mobile/openapi/lib/model/album_response_dto.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										94
									
								
								mobile/openapi/lib/model/album_response_dto.dart
									
									
									
										generated
									
									
									
								
							| @@ -43,51 +43,48 @@ class AlbumResponseDto { | ||||
|   List<AssetResponseDto> assets; | ||||
| 
 | ||||
|   @override | ||||
|   bool operator ==(Object other) => | ||||
|       identical(this, other) || | ||||
|       other is AlbumResponseDto && | ||||
|           other.assetCount == assetCount && | ||||
|           other.id == id && | ||||
|           other.ownerId == ownerId && | ||||
|           other.albumName == albumName && | ||||
|           other.createdAt == createdAt && | ||||
|           other.albumThumbnailAssetId == albumThumbnailAssetId && | ||||
|           other.shared == shared && | ||||
|           other.sharedUsers == sharedUsers && | ||||
|           other.assets == assets; | ||||
|   bool operator ==(Object other) => identical(this, other) || other is AlbumResponseDto && | ||||
|      other.assetCount == assetCount && | ||||
|      other.id == id && | ||||
|      other.ownerId == ownerId && | ||||
|      other.albumName == albumName && | ||||
|      other.createdAt == createdAt && | ||||
|      other.albumThumbnailAssetId == albumThumbnailAssetId && | ||||
|      other.shared == shared && | ||||
|      other.sharedUsers == sharedUsers && | ||||
|      other.assets == assets; | ||||
| 
 | ||||
|   @override | ||||
|   int get hashCode => | ||||
|       // ignore: unnecessary_parenthesis | ||||
|       (assetCount.hashCode) + | ||||
|       (id.hashCode) + | ||||
|       (ownerId.hashCode) + | ||||
|       (albumName.hashCode) + | ||||
|       (createdAt.hashCode) + | ||||
|       (albumThumbnailAssetId == null ? 0 : albumThumbnailAssetId!.hashCode) + | ||||
|       (shared.hashCode) + | ||||
|       (sharedUsers.hashCode) + | ||||
|       (assets.hashCode); | ||||
|     // ignore: unnecessary_parenthesis | ||||
|     (assetCount.hashCode) + | ||||
|     (id.hashCode) + | ||||
|     (ownerId.hashCode) + | ||||
|     (albumName.hashCode) + | ||||
|     (createdAt.hashCode) + | ||||
|     (albumThumbnailAssetId == null ? 0 : albumThumbnailAssetId!.hashCode) + | ||||
|     (shared.hashCode) + | ||||
|     (sharedUsers.hashCode) + | ||||
|     (assets.hashCode); | ||||
| 
 | ||||
|   @override | ||||
|   String toString() => | ||||
|       'AlbumResponseDto[assetCount=$assetCount, id=$id, ownerId=$ownerId, albumName=$albumName, createdAt=$createdAt, albumThumbnailAssetId=$albumThumbnailAssetId, shared=$shared, sharedUsers=$sharedUsers, assets=$assets]'; | ||||
|   String toString() => 'AlbumResponseDto[assetCount=$assetCount, id=$id, ownerId=$ownerId, albumName=$albumName, createdAt=$createdAt, albumThumbnailAssetId=$albumThumbnailAssetId, shared=$shared, sharedUsers=$sharedUsers, assets=$assets]'; | ||||
| 
 | ||||
|   Map<String, dynamic> toJson() { | ||||
|     final json = <String, dynamic>{}; | ||||
|     json[r'assetCount'] = this.assetCount; | ||||
|     json[r'id'] = this.id; | ||||
|     json[r'ownerId'] = this.ownerId; | ||||
|     json[r'albumName'] = this.albumName; | ||||
|     json[r'createdAt'] = this.createdAt; | ||||
|       json[r'assetCount'] = this.assetCount; | ||||
|       json[r'id'] = this.id; | ||||
|       json[r'ownerId'] = this.ownerId; | ||||
|       json[r'albumName'] = this.albumName; | ||||
|       json[r'createdAt'] = this.createdAt; | ||||
|     if (this.albumThumbnailAssetId != null) { | ||||
|       json[r'albumThumbnailAssetId'] = this.albumThumbnailAssetId; | ||||
|     } else { | ||||
|       // json[r'albumThumbnailAssetId'] = null; | ||||
|     } | ||||
|     json[r'shared'] = this.shared; | ||||
|     json[r'sharedUsers'] = this.sharedUsers; | ||||
|     json[r'assets'] = this.assets; | ||||
|       json[r'shared'] = this.shared; | ||||
|       json[r'sharedUsers'] = this.sharedUsers; | ||||
|       json[r'assets'] = this.assets; | ||||
|     return json; | ||||
|   } | ||||
| 
 | ||||
| @@ -101,13 +98,13 @@ class AlbumResponseDto { | ||||
|       // Ensure that the map contains the required keys. | ||||
|       // Note 1: the values aren't checked for validity beyond being non-null. | ||||
|       // Note 2: this code is stripped in release mode! | ||||
|       // assert(() { | ||||
|       //   requiredKeys.forEach((key) { | ||||
|       //     assert(json.containsKey(key), 'Required key "AlbumResponseDto[$key]" is missing from JSON.'); | ||||
|       //     assert(json[key] != null, 'Required key "AlbumResponseDto[$key]" has a null value in JSON.'); | ||||
|       //   }); | ||||
|       //   return true; | ||||
|       // }()); | ||||
|       assert(() { | ||||
|         requiredKeys.forEach((key) { | ||||
|           assert(json.containsKey(key), 'Required key "AlbumResponseDto[$key]" is missing from JSON.'); | ||||
|           assert(json[key] != null, 'Required key "AlbumResponseDto[$key]" has a null value in JSON.'); | ||||
|         }); | ||||
|         return true; | ||||
|       }()); | ||||
| 
 | ||||
|       return AlbumResponseDto( | ||||
|         assetCount: mapValueOfType<int>(json, r'assetCount')!, | ||||
| @@ -115,8 +112,7 @@ class AlbumResponseDto { | ||||
|         ownerId: mapValueOfType<String>(json, r'ownerId')!, | ||||
|         albumName: mapValueOfType<String>(json, r'albumName')!, | ||||
|         createdAt: mapValueOfType<String>(json, r'createdAt')!, | ||||
|         albumThumbnailAssetId: | ||||
|             mapValueOfType<String>(json, r'albumThumbnailAssetId'), | ||||
|         albumThumbnailAssetId: mapValueOfType<String>(json, r'albumThumbnailAssetId'), | ||||
|         shared: mapValueOfType<bool>(json, r'shared')!, | ||||
|         sharedUsers: UserResponseDto.listFromJson(json[r'sharedUsers'])!, | ||||
|         assets: AssetResponseDto.listFromJson(json[r'assets'])!, | ||||
| @@ -125,10 +121,7 @@ class AlbumResponseDto { | ||||
|     return null; | ||||
|   } | ||||
| 
 | ||||
|   static List<AlbumResponseDto>? listFromJson( | ||||
|     dynamic json, { | ||||
|     bool growable = false, | ||||
|   }) { | ||||
|   static List<AlbumResponseDto>? listFromJson(dynamic json, {bool growable = false,}) { | ||||
|     final result = <AlbumResponseDto>[]; | ||||
|     if (json is List && json.isNotEmpty) { | ||||
|       for (final row in json) { | ||||
| @@ -156,18 +149,12 @@ class AlbumResponseDto { | ||||
|   } | ||||
| 
 | ||||
|   // maps a json object with a list of AlbumResponseDto-objects as value to a dart map | ||||
|   static Map<String, List<AlbumResponseDto>> mapListFromJson( | ||||
|     dynamic json, { | ||||
|     bool growable = false, | ||||
|   }) { | ||||
|   static Map<String, List<AlbumResponseDto>> mapListFromJson(dynamic json, {bool growable = false,}) { | ||||
|     final map = <String, List<AlbumResponseDto>>{}; | ||||
|     if (json is Map && json.isNotEmpty) { | ||||
|       json = json.cast<String, dynamic>(); // ignore: parameter_assignments | ||||
|       for (final entry in json.entries) { | ||||
|         final value = AlbumResponseDto.listFromJson( | ||||
|           entry.value, | ||||
|           growable: growable, | ||||
|         ); | ||||
|         final value = AlbumResponseDto.listFromJson(entry.value, growable: growable,); | ||||
|         if (value != null) { | ||||
|           map[entry.key] = value; | ||||
|         } | ||||
| @@ -189,3 +176,4 @@ class AlbumResponseDto { | ||||
|     'assets', | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
|   | ||||
							
								
								
									
										133
									
								
								mobile/openapi/lib/model/asset_response_dto.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										133
									
								
								mobile/openapi/lib/model/asset_response_dto.dart
									
									
									
										generated
									
									
									
								
							| @@ -82,76 +82,73 @@ class AssetResponseDto { | ||||
|   List<TagResponseDto> tags; | ||||
| 
 | ||||
|   @override | ||||
|   bool operator ==(Object other) => | ||||
|       identical(this, other) || | ||||
|       other is AssetResponseDto && | ||||
|           other.type == type && | ||||
|           other.id == id && | ||||
|           other.deviceAssetId == deviceAssetId && | ||||
|           other.ownerId == ownerId && | ||||
|           other.deviceId == deviceId && | ||||
|           other.originalPath == originalPath && | ||||
|           other.resizePath == resizePath && | ||||
|           other.createdAt == createdAt && | ||||
|           other.modifiedAt == modifiedAt && | ||||
|           other.isFavorite == isFavorite && | ||||
|           other.mimeType == mimeType && | ||||
|           other.duration == duration && | ||||
|           other.webpPath == webpPath && | ||||
|           other.encodedVideoPath == encodedVideoPath && | ||||
|           other.exifInfo == exifInfo && | ||||
|           other.smartInfo == smartInfo && | ||||
|           other.livePhotoVideoId == livePhotoVideoId && | ||||
|           other.tags == tags; | ||||
|   bool operator ==(Object other) => identical(this, other) || other is AssetResponseDto && | ||||
|      other.type == type && | ||||
|      other.id == id && | ||||
|      other.deviceAssetId == deviceAssetId && | ||||
|      other.ownerId == ownerId && | ||||
|      other.deviceId == deviceId && | ||||
|      other.originalPath == originalPath && | ||||
|      other.resizePath == resizePath && | ||||
|      other.createdAt == createdAt && | ||||
|      other.modifiedAt == modifiedAt && | ||||
|      other.isFavorite == isFavorite && | ||||
|      other.mimeType == mimeType && | ||||
|      other.duration == duration && | ||||
|      other.webpPath == webpPath && | ||||
|      other.encodedVideoPath == encodedVideoPath && | ||||
|      other.exifInfo == exifInfo && | ||||
|      other.smartInfo == smartInfo && | ||||
|      other.livePhotoVideoId == livePhotoVideoId && | ||||
|      other.tags == tags; | ||||
| 
 | ||||
|   @override | ||||
|   int get hashCode => | ||||
|       // ignore: unnecessary_parenthesis | ||||
|       (type.hashCode) + | ||||
|       (id.hashCode) + | ||||
|       (deviceAssetId.hashCode) + | ||||
|       (ownerId.hashCode) + | ||||
|       (deviceId.hashCode) + | ||||
|       (originalPath.hashCode) + | ||||
|       (resizePath == null ? 0 : resizePath!.hashCode) + | ||||
|       (createdAt.hashCode) + | ||||
|       (modifiedAt.hashCode) + | ||||
|       (isFavorite.hashCode) + | ||||
|       (mimeType == null ? 0 : mimeType!.hashCode) + | ||||
|       (duration.hashCode) + | ||||
|       (webpPath == null ? 0 : webpPath!.hashCode) + | ||||
|       (encodedVideoPath == null ? 0 : encodedVideoPath!.hashCode) + | ||||
|       (exifInfo == null ? 0 : exifInfo!.hashCode) + | ||||
|       (smartInfo == null ? 0 : smartInfo!.hashCode) + | ||||
|       (livePhotoVideoId == null ? 0 : livePhotoVideoId!.hashCode) + | ||||
|       (tags.hashCode); | ||||
|     // ignore: unnecessary_parenthesis | ||||
|     (type.hashCode) + | ||||
|     (id.hashCode) + | ||||
|     (deviceAssetId.hashCode) + | ||||
|     (ownerId.hashCode) + | ||||
|     (deviceId.hashCode) + | ||||
|     (originalPath.hashCode) + | ||||
|     (resizePath == null ? 0 : resizePath!.hashCode) + | ||||
|     (createdAt.hashCode) + | ||||
|     (modifiedAt.hashCode) + | ||||
|     (isFavorite.hashCode) + | ||||
|     (mimeType == null ? 0 : mimeType!.hashCode) + | ||||
|     (duration.hashCode) + | ||||
|     (webpPath == null ? 0 : webpPath!.hashCode) + | ||||
|     (encodedVideoPath == null ? 0 : encodedVideoPath!.hashCode) + | ||||
|     (exifInfo == null ? 0 : exifInfo!.hashCode) + | ||||
|     (smartInfo == null ? 0 : smartInfo!.hashCode) + | ||||
|     (livePhotoVideoId == null ? 0 : livePhotoVideoId!.hashCode) + | ||||
|     (tags.hashCode); | ||||
| 
 | ||||
|   @override | ||||
|   String toString() => | ||||
|       'AssetResponseDto[type=$type, id=$id, deviceAssetId=$deviceAssetId, ownerId=$ownerId, deviceId=$deviceId, originalPath=$originalPath, resizePath=$resizePath, createdAt=$createdAt, modifiedAt=$modifiedAt, isFavorite=$isFavorite, mimeType=$mimeType, duration=$duration, webpPath=$webpPath, encodedVideoPath=$encodedVideoPath, exifInfo=$exifInfo, smartInfo=$smartInfo, livePhotoVideoId=$livePhotoVideoId, tags=$tags]'; | ||||
|   String toString() => 'AssetResponseDto[type=$type, id=$id, deviceAssetId=$deviceAssetId, ownerId=$ownerId, deviceId=$deviceId, originalPath=$originalPath, resizePath=$resizePath, createdAt=$createdAt, modifiedAt=$modifiedAt, isFavorite=$isFavorite, mimeType=$mimeType, duration=$duration, webpPath=$webpPath, encodedVideoPath=$encodedVideoPath, exifInfo=$exifInfo, smartInfo=$smartInfo, livePhotoVideoId=$livePhotoVideoId, tags=$tags]'; | ||||
| 
 | ||||
|   Map<String, dynamic> toJson() { | ||||
|     final json = <String, dynamic>{}; | ||||
|     json[r'type'] = this.type; | ||||
|     json[r'id'] = this.id; | ||||
|     json[r'deviceAssetId'] = this.deviceAssetId; | ||||
|     json[r'ownerId'] = this.ownerId; | ||||
|     json[r'deviceId'] = this.deviceId; | ||||
|     json[r'originalPath'] = this.originalPath; | ||||
|       json[r'type'] = this.type; | ||||
|       json[r'id'] = this.id; | ||||
|       json[r'deviceAssetId'] = this.deviceAssetId; | ||||
|       json[r'ownerId'] = this.ownerId; | ||||
|       json[r'deviceId'] = this.deviceId; | ||||
|       json[r'originalPath'] = this.originalPath; | ||||
|     if (this.resizePath != null) { | ||||
|       json[r'resizePath'] = this.resizePath; | ||||
|     } else { | ||||
|       // json[r'resizePath'] = null; | ||||
|     } | ||||
|     json[r'createdAt'] = this.createdAt; | ||||
|     json[r'modifiedAt'] = this.modifiedAt; | ||||
|     json[r'isFavorite'] = this.isFavorite; | ||||
|       json[r'createdAt'] = this.createdAt; | ||||
|       json[r'modifiedAt'] = this.modifiedAt; | ||||
|       json[r'isFavorite'] = this.isFavorite; | ||||
|     if (this.mimeType != null) { | ||||
|       json[r'mimeType'] = this.mimeType; | ||||
|     } else { | ||||
|       // json[r'mimeType'] = null; | ||||
|     } | ||||
|     json[r'duration'] = this.duration; | ||||
|       json[r'duration'] = this.duration; | ||||
|     if (this.webpPath != null) { | ||||
|       json[r'webpPath'] = this.webpPath; | ||||
|     } else { | ||||
| @@ -177,7 +174,7 @@ class AssetResponseDto { | ||||
|     } else { | ||||
|       // json[r'livePhotoVideoId'] = null; | ||||
|     } | ||||
|     json[r'tags'] = this.tags; | ||||
|       json[r'tags'] = this.tags; | ||||
|     return json; | ||||
|   } | ||||
| 
 | ||||
| @@ -191,13 +188,13 @@ class AssetResponseDto { | ||||
|       // Ensure that the map contains the required keys. | ||||
|       // Note 1: the values aren't checked for validity beyond being non-null. | ||||
|       // Note 2: this code is stripped in release mode! | ||||
|       // assert(() { | ||||
|       //   requiredKeys.forEach((key) { | ||||
|       //     assert(json.containsKey(key), 'Required key "AssetResponseDto[$key]" is missing from JSON.'); | ||||
|       //     assert(json[key] != null, 'Required key "AssetResponseDto[$key]" has a null value in JSON.'); | ||||
|       //   }); | ||||
|       //   return true; | ||||
|       // }()); | ||||
|       assert(() { | ||||
|         requiredKeys.forEach((key) { | ||||
|           assert(json.containsKey(key), 'Required key "AssetResponseDto[$key]" is missing from JSON.'); | ||||
|           assert(json[key] != null, 'Required key "AssetResponseDto[$key]" has a null value in JSON.'); | ||||
|         }); | ||||
|         return true; | ||||
|       }()); | ||||
| 
 | ||||
|       return AssetResponseDto( | ||||
|         type: AssetTypeEnum.fromJson(json[r'type'])!, | ||||
| @@ -223,10 +220,7 @@ class AssetResponseDto { | ||||
|     return null; | ||||
|   } | ||||
| 
 | ||||
|   static List<AssetResponseDto>? listFromJson( | ||||
|     dynamic json, { | ||||
|     bool growable = false, | ||||
|   }) { | ||||
|   static List<AssetResponseDto>? listFromJson(dynamic json, {bool growable = false,}) { | ||||
|     final result = <AssetResponseDto>[]; | ||||
|     if (json is List && json.isNotEmpty) { | ||||
|       for (final row in json) { | ||||
| @@ -254,18 +248,12 @@ class AssetResponseDto { | ||||
|   } | ||||
| 
 | ||||
|   // maps a json object with a list of AssetResponseDto-objects as value to a dart map | ||||
|   static Map<String, List<AssetResponseDto>> mapListFromJson( | ||||
|     dynamic json, { | ||||
|     bool growable = false, | ||||
|   }) { | ||||
|   static Map<String, List<AssetResponseDto>> mapListFromJson(dynamic json, {bool growable = false,}) { | ||||
|     final map = <String, List<AssetResponseDto>>{}; | ||||
|     if (json is Map && json.isNotEmpty) { | ||||
|       json = json.cast<String, dynamic>(); // ignore: parameter_assignments | ||||
|       for (final entry in json.entries) { | ||||
|         final value = AssetResponseDto.listFromJson( | ||||
|           entry.value, | ||||
|           growable: growable, | ||||
|         ); | ||||
|         final value = AssetResponseDto.listFromJson(entry.value, growable: growable,); | ||||
|         if (value != null) { | ||||
|           map[entry.key] = value; | ||||
|         } | ||||
| @@ -292,3 +280,4 @@ class AssetResponseDto { | ||||
|     'tags', | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
|   | ||||
							
								
								
									
										2
									
								
								mobile/openapi/test/asset_api_test.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2
									
								
								mobile/openapi/test/asset_api_test.dart
									
									
									
										generated
									
									
									
								
							| @@ -47,7 +47,7 @@ void main() { | ||||
| 
 | ||||
|     //  | ||||
|     // | ||||
|     //Future<Object> downloadFile(String assetId, { bool isThumb, bool isWeb }) async | ||||
|     //Future<Object> downloadFile(String assetId) async | ||||
|     test('test downloadFile', () async { | ||||
|       // TODO | ||||
|     }); | ||||
|   | ||||
| @@ -15,6 +15,7 @@ import { | ||||
|   Put, | ||||
|   UploadedFiles, | ||||
|   Patch, | ||||
|   StreamableFile, | ||||
| } from '@nestjs/common'; | ||||
| import { Authenticated } from '../../decorators/authenticated.decorator'; | ||||
| import { AssetService } from './asset.service'; | ||||
| @@ -28,7 +29,7 @@ import { CheckDuplicateAssetDto } from './dto/check-duplicate-asset.dto'; | ||||
| import { ApiBearerAuth, ApiBody, ApiConsumes, ApiHeader, ApiTags } from '@nestjs/swagger'; | ||||
| import { CuratedObjectsResponseDto } from './response-dto/curated-objects-response.dto'; | ||||
| import { CuratedLocationsResponseDto } from './response-dto/curated-locations-response.dto'; | ||||
| import { AssetResponseDto } from '@app/domain'; | ||||
| import { AssetResponseDto, ImmichReadStream } from '@app/domain'; | ||||
| import { CheckDuplicateAssetResponseDto } from './response-dto/check-duplicate-asset-response.dto'; | ||||
| import { AssetFileUploadDto } from './dto/asset-file-upload.dto'; | ||||
| import { CreateAssetDto, mapToUploadFile } from './dto/create-asset.dto'; | ||||
| @@ -55,6 +56,10 @@ import { UpdateAssetsToSharedLinkDto } from './dto/add-assets-to-shared-link.dto | ||||
| import { AssetSearchDto } from './dto/asset-search.dto'; | ||||
| import { assetUploadOption, ImmichFile } from '../../config/asset-upload.config'; | ||||
|  | ||||
| function asStreamableFile({ stream, type, length }: ImmichReadStream) { | ||||
|   return new StreamableFile(stream, { type, length }); | ||||
| } | ||||
|  | ||||
| @ApiBearerAuth() | ||||
| @ApiTags('Asset') | ||||
| @Controller('asset') | ||||
| @@ -103,12 +108,9 @@ export class AssetController { | ||||
|   async downloadFile( | ||||
|     @GetAuthUser() authUser: AuthUserDto, | ||||
|     @Response({ passthrough: true }) res: Res, | ||||
|     @Query(new ValidationPipe({ transform: true })) query: ServeFileDto, | ||||
|     @Param('assetId') assetId: string, | ||||
|   ): Promise<any> { | ||||
|     this.assetService.checkDownloadAccess(authUser); | ||||
|     await this.assetService.checkAssetsAccess(authUser, [assetId]); | ||||
|     return this.assetService.downloadFile(query, assetId, res); | ||||
|     return this.assetService.downloadFile(authUser, assetId).then(asStreamableFile); | ||||
|   } | ||||
|  | ||||
|   @Authenticated({ isShared: true }) | ||||
|   | ||||
| @@ -9,12 +9,13 @@ import { AssetCountByUserIdResponseDto } from './response-dto/asset-count-by-use | ||||
| import { DownloadService } from '../../modules/download/download.service'; | ||||
| import { AlbumRepository, IAlbumRepository } from '../album/album-repository'; | ||||
| import { StorageService } from '@app/storage'; | ||||
| import { ICryptoRepository, IJobRepository, ISharedLinkRepository, JobName } from '@app/domain'; | ||||
| import { ICryptoRepository, IJobRepository, ISharedLinkRepository, IStorageRepository, JobName } from '@app/domain'; | ||||
| import { | ||||
|   authStub, | ||||
|   newCryptoRepositoryMock, | ||||
|   newJobRepositoryMock, | ||||
|   newSharedLinkRepositoryMock, | ||||
|   newStorageRepositoryMock, | ||||
|   sharedLinkResponseStub, | ||||
|   sharedLinkStub, | ||||
| } from '@app/domain/../test'; | ||||
| @@ -110,6 +111,7 @@ describe('AssetService', () => { | ||||
|   let sharedLinkRepositoryMock: jest.Mocked<ISharedLinkRepository>; | ||||
|   let cryptoMock: jest.Mocked<ICryptoRepository>; | ||||
|   let jobMock: jest.Mocked<IJobRepository>; | ||||
|   let storageMock: jest.Mocked<IStorageRepository>; | ||||
|  | ||||
|   beforeEach(() => { | ||||
|     assetRepositoryMock = { | ||||
| @@ -154,6 +156,7 @@ describe('AssetService', () => { | ||||
|     sharedLinkRepositoryMock = newSharedLinkRepositoryMock(); | ||||
|     jobMock = newJobRepositoryMock(); | ||||
|     cryptoMock = newCryptoRepositoryMock(); | ||||
|     storageMock = newStorageRepositoryMock(); | ||||
|  | ||||
|     sut = new AssetService( | ||||
|       assetRepositoryMock, | ||||
| @@ -164,6 +167,7 @@ describe('AssetService', () => { | ||||
|       sharedLinkRepositoryMock, | ||||
|       jobMock, | ||||
|       cryptoMock, | ||||
|       storageMock, | ||||
|     ); | ||||
|   }); | ||||
|  | ||||
| @@ -413,4 +417,15 @@ describe('AssetService', () => { | ||||
|       expect(() => sut.checkDownloadAccess(authStub.readonlySharedLink)).toThrow(ForbiddenException); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('downloadFile', () => { | ||||
|     it('should download a single file', async () => { | ||||
|       assetRepositoryMock.countByIdAndUser.mockResolvedValue(1); | ||||
|       assetRepositoryMock.get.mockResolvedValue(_getAsset_1()); | ||||
|  | ||||
|       await sut.downloadFile(authStub.admin, 'id_1'); | ||||
|  | ||||
|       expect(storageMock.createReadStream).toHaveBeenCalledWith('fake_path/asset_1.jpeg', 'image/jpeg'); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
|   | ||||
| @@ -10,7 +10,6 @@ import { | ||||
|   StreamableFile, | ||||
| } from '@nestjs/common'; | ||||
| import { InjectRepository } from '@nestjs/typeorm'; | ||||
| import { createHash } from 'node:crypto'; | ||||
| import { QueryFailedError, Repository } from 'typeorm'; | ||||
| import { AuthUserDto } from '../../decorators/auth-user.decorator'; | ||||
| import { AssetEntity, AssetType, SharedLinkType } from '@app/infra'; | ||||
| @@ -23,7 +22,14 @@ import { SearchAssetDto } from './dto/search-asset.dto'; | ||||
| import fs from 'fs/promises'; | ||||
| import { CheckDuplicateAssetDto } from './dto/check-duplicate-asset.dto'; | ||||
| import { CuratedObjectsResponseDto } from './response-dto/curated-objects-response.dto'; | ||||
| import { AssetResponseDto, JobName, mapAsset, mapAssetWithoutExif } from '@app/domain'; | ||||
| import { | ||||
|   AssetResponseDto, | ||||
|   ImmichReadStream, | ||||
|   IStorageRepository, | ||||
|   JobName, | ||||
|   mapAsset, | ||||
|   mapAssetWithoutExif, | ||||
| } from '@app/domain'; | ||||
| import { CreateAssetDto, UploadFile } from './dto/create-asset.dto'; | ||||
| import { DeleteAssetResponseDto, DeleteAssetStatusEnum } from './response-dto/delete-asset-response.dto'; | ||||
| import { GetAssetThumbnailDto, GetAssetThumbnailFormatEnum } from './dto/get-asset-thumbnail.dto'; | ||||
| @@ -73,6 +79,7 @@ export class AssetService { | ||||
|     @Inject(ISharedLinkRepository) sharedLinkRepository: ISharedLinkRepository, | ||||
|     @Inject(IJobRepository) private jobRepository: IJobRepository, | ||||
|     @Inject(ICryptoRepository) cryptoRepository: ICryptoRepository, | ||||
|     @Inject(IStorageRepository) private storage: IStorageRepository, | ||||
|   ) { | ||||
|     this.assetCore = new AssetCore(_assetRepository, jobRepository, storageService); | ||||
|     this.shareCore = new ShareCore(sharedLinkRepository, cryptoRepository); | ||||
| @@ -189,62 +196,21 @@ export class AssetService { | ||||
|     return this.downloadService.downloadArchive(`immich-${now}`, assetToDownload); | ||||
|   } | ||||
|  | ||||
|   public async downloadFile(query: ServeFileDto, assetId: string, res: Res) { | ||||
|   public async downloadFile(authUser: AuthUserDto, assetId: string): Promise<ImmichReadStream> { | ||||
|     this.checkDownloadAccess(authUser); | ||||
|     await this.checkAssetsAccess(authUser, [assetId]); | ||||
|  | ||||
|     try { | ||||
|       let fileReadStream = null; | ||||
|       const asset = await this._assetRepository.getById(assetId); | ||||
|  | ||||
|       // Download Video | ||||
|       if (asset.type === AssetType.VIDEO) { | ||||
|         const { size } = await fileInfo(asset.originalPath); | ||||
|  | ||||
|         res.set({ | ||||
|           'Content-Type': asset.mimeType, | ||||
|           'Content-Length': size, | ||||
|         }); | ||||
|  | ||||
|         await fs.access(asset.originalPath, constants.R_OK | constants.W_OK); | ||||
|         fileReadStream = createReadStream(asset.originalPath); | ||||
|       } else { | ||||
|         // Download Image | ||||
|         if (!query.isThumb) { | ||||
|           /** | ||||
|            * Download Image Original File | ||||
|            */ | ||||
|           const { size } = await fileInfo(asset.originalPath); | ||||
|  | ||||
|           res.set({ | ||||
|             'Content-Type': asset.mimeType, | ||||
|             'Content-Length': size, | ||||
|           }); | ||||
|  | ||||
|           await fs.access(asset.originalPath, constants.R_OK | constants.W_OK); | ||||
|           fileReadStream = createReadStream(asset.originalPath); | ||||
|         } else { | ||||
|           /** | ||||
|            * Download Image Resize File | ||||
|            */ | ||||
|           if (!asset.resizePath) { | ||||
|             throw new NotFoundException('resizePath not set'); | ||||
|           } | ||||
|  | ||||
|           const { size } = await fileInfo(asset.resizePath); | ||||
|  | ||||
|           res.set({ | ||||
|             'Content-Type': 'image/jpeg', | ||||
|             'Content-Length': size, | ||||
|           }); | ||||
|  | ||||
|           await fs.access(asset.resizePath, constants.R_OK | constants.W_OK); | ||||
|           fileReadStream = createReadStream(asset.resizePath); | ||||
|         } | ||||
|       const asset = await this._assetRepository.get(assetId); | ||||
|       if (asset && asset.originalPath && asset.mimeType) { | ||||
|         return this.storage.createReadStream(asset.originalPath, asset.mimeType); | ||||
|       } | ||||
|  | ||||
|       return new StreamableFile(fileReadStream); | ||||
|     } catch (e) { | ||||
|       Logger.error(`Error download asset ${e}`, 'downloadFile'); | ||||
|       throw new InternalServerErrorException(`Failed to download asset ${e}`, 'DownloadFile'); | ||||
|     } | ||||
|  | ||||
|     throw new NotFoundException(); | ||||
|   } | ||||
|  | ||||
|   public async getAssetThumbnail( | ||||
| @@ -255,8 +221,7 @@ export class AssetService { | ||||
|   ) { | ||||
|     let fileReadStream: ReadStream; | ||||
|  | ||||
|     const asset = await this.assetRepository.findOne({ where: { id: assetId } }); | ||||
|  | ||||
|     const asset = await this._assetRepository.get(assetId); | ||||
|     if (!asset) { | ||||
|       throw new NotFoundException('Asset not found'); | ||||
|     } | ||||
| @@ -584,18 +549,6 @@ export class AssetService { | ||||
|     return this._assetRepository.getAssetByChecksum(userId, checksum); | ||||
|   } | ||||
|  | ||||
|   calculateChecksum(filePath: string): Promise<Buffer> { | ||||
|     const fileReadStream = createReadStream(filePath); | ||||
|     const sha1Hash = createHash('sha1'); | ||||
|     const deferred = new Promise<Buffer>((resolve, reject) => { | ||||
|       sha1Hash.once('error', (err) => reject(err)); | ||||
|       sha1Hash.once('finish', () => resolve(sha1Hash.read())); | ||||
|     }); | ||||
|  | ||||
|     fileReadStream.pipe(sha1Hash); | ||||
|     return deferred; | ||||
|   } | ||||
|  | ||||
|   getAssetCountByUserId(authUser: AuthUserDto): Promise<AssetCountByUserIdResponseDto> { | ||||
|     return this._assetRepository.getAssetCountByUserId(authUser.id); | ||||
|   } | ||||
|   | ||||
| @@ -1109,24 +1109,6 @@ | ||||
|         "operationId": "downloadFile", | ||||
|         "description": "", | ||||
|         "parameters": [ | ||||
|           { | ||||
|             "name": "isThumb", | ||||
|             "required": false, | ||||
|             "in": "query", | ||||
|             "schema": { | ||||
|               "title": "Is serve thumbnail (resize) file", | ||||
|               "type": "boolean" | ||||
|             } | ||||
|           }, | ||||
|           { | ||||
|             "name": "isWeb", | ||||
|             "required": false, | ||||
|             "in": "query", | ||||
|             "schema": { | ||||
|               "title": "Is request made from web", | ||||
|               "type": "boolean" | ||||
|             } | ||||
|           }, | ||||
|           { | ||||
|             "name": "assetId", | ||||
|             "required": true, | ||||
|   | ||||
| @@ -6,11 +6,6 @@ import { ICryptoRepository } from '../crypto/crypto.repository'; | ||||
| import { LoginResponseDto, mapLoginResponse } from './response-dto'; | ||||
| import { IUserTokenRepository, UserTokenCore } from '../user-token'; | ||||
|  | ||||
| export type JwtValidationResult = { | ||||
|   status: boolean; | ||||
|   userId: string | null; | ||||
| }; | ||||
|  | ||||
| export class AuthCore { | ||||
|   private userTokenCore: UserTokenCore; | ||||
|   constructor( | ||||
|   | ||||
| @@ -1,5 +1,4 @@ | ||||
| export * from './auth-user.dto'; | ||||
| export * from './change-password.dto'; | ||||
| export * from './jwt-payload.dto'; | ||||
| export * from './login-credential.dto'; | ||||
| export * from './sign-up.dto'; | ||||
|   | ||||
| @@ -1,4 +0,0 @@ | ||||
| export class JwtPayloadDto { | ||||
|   userId!: string; | ||||
|   email!: string; | ||||
| } | ||||
| @@ -8,6 +8,7 @@ export * from './domain.module'; | ||||
| export * from './job'; | ||||
| export * from './oauth'; | ||||
| export * from './share'; | ||||
| export * from './storage'; | ||||
| export * from './system-config'; | ||||
| export * from './tag'; | ||||
| export * from './user'; | ||||
|   | ||||
							
								
								
									
										1
									
								
								server/libs/domain/src/storage/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								server/libs/domain/src/storage/index.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| export * from './storage.repository'; | ||||
							
								
								
									
										13
									
								
								server/libs/domain/src/storage/storage.repository.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								server/libs/domain/src/storage/storage.repository.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| import { ReadStream } from 'fs'; | ||||
|  | ||||
| export interface ImmichReadStream { | ||||
|   stream: ReadStream; | ||||
|   type: string; | ||||
|   length: number; | ||||
| } | ||||
|  | ||||
| export const IStorageRepository = 'IStorageRepository'; | ||||
|  | ||||
| export interface IStorageRepository { | ||||
|   createReadStream(filepath: string, mimeType: string): Promise<ImmichReadStream>; | ||||
| } | ||||
| @@ -4,6 +4,7 @@ export * from './device-info.repository.mock'; | ||||
| export * from './fixtures'; | ||||
| export * from './job.repository.mock'; | ||||
| export * from './shared-link.repository.mock'; | ||||
| export * from './storage.repository.mock'; | ||||
| export * from './system-config.repository.mock'; | ||||
| export * from './user-token.repository.mock'; | ||||
| export * from './user.repository.mock'; | ||||
|   | ||||
							
								
								
									
										7
									
								
								server/libs/domain/test/storage.repository.mock.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								server/libs/domain/test/storage.repository.mock.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| import { IStorageRepository } from '../src'; | ||||
|  | ||||
| export const newStorageRepositoryMock = (): jest.Mocked<IStorageRepository> => { | ||||
|   return { | ||||
|     createReadStream: jest.fn(), | ||||
|   }; | ||||
| }; | ||||
| @@ -4,6 +4,7 @@ import { | ||||
|   IJobRepository, | ||||
|   IKeyRepository, | ||||
|   ISharedLinkRepository, | ||||
|   IStorageRepository, | ||||
|   ISystemConfigRepository, | ||||
|   IUserRepository, | ||||
|   QueueName, | ||||
| @@ -29,6 +30,7 @@ import { | ||||
|   UserTokenEntity, | ||||
| } from './db'; | ||||
| import { JobRepository } from './job'; | ||||
| import { FilesystemProvider } from './storage'; | ||||
|  | ||||
| const providers: Provider[] = [ | ||||
|   { provide: ICryptoRepository, useClass: CryptoRepository }, | ||||
| @@ -36,6 +38,7 @@ const providers: Provider[] = [ | ||||
|   { provide: IKeyRepository, useClass: APIKeyRepository }, | ||||
|   { provide: IJobRepository, useClass: JobRepository }, | ||||
|   { provide: ISharedLinkRepository, useClass: SharedLinkRepository }, | ||||
|   { provide: IStorageRepository, useClass: FilesystemProvider }, | ||||
|   { provide: ISystemConfigRepository, useClass: SystemConfigRepository }, | ||||
|   { provide: IUserRepository, useClass: UserRepository }, | ||||
|   { provide: IUserTokenRepository, useClass: UserTokenRepository }, | ||||
|   | ||||
							
								
								
									
										18
									
								
								server/libs/infra/src/storage/filesystem.provider.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								server/libs/infra/src/storage/filesystem.provider.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| import { ImmichReadStream, IStorageRepository } from '@app/domain'; | ||||
| import { constants, createReadStream, stat } from 'fs'; | ||||
| import fs from 'fs/promises'; | ||||
| import { promisify } from 'util'; | ||||
|  | ||||
| const fileInfo = promisify(stat); | ||||
|  | ||||
| export class FilesystemProvider implements IStorageRepository { | ||||
|   async createReadStream(filepath: string, mimeType: string): Promise<ImmichReadStream> { | ||||
|     const { size } = await fileInfo(filepath); | ||||
|     await fs.access(filepath, constants.R_OK | constants.W_OK); | ||||
|     return { | ||||
|       stream: createReadStream(filepath), | ||||
|       length: size, | ||||
|       type: mimeType, | ||||
|     }; | ||||
|   } | ||||
| } | ||||
							
								
								
									
										1
									
								
								server/libs/infra/src/storage/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								server/libs/infra/src/storage/index.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| export * from './filesystem.provider'; | ||||
							
								
								
									
										32
									
								
								web/src/api/open-api/api.ts
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										32
									
								
								web/src/api/open-api/api.ts
									
									
									
										generated
									
									
									
								
							| @@ -4,7 +4,7 @@ | ||||
|  * Immich | ||||
|  * Immich API | ||||
|  * | ||||
|  * The version of the OpenAPI document: 1.43.0 | ||||
|  * The version of the OpenAPI document: 1.43.1 | ||||
|  *  | ||||
|  * | ||||
|  * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
 | ||||
| @@ -3729,12 +3729,10 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration | ||||
|         /** | ||||
|          *  | ||||
|          * @param {string} assetId  | ||||
|          * @param {boolean} [isThumb]  | ||||
|          * @param {boolean} [isWeb]  | ||||
|          * @param {*} [options] Override http request option. | ||||
|          * @throws {RequiredError} | ||||
|          */ | ||||
|         downloadFile: async (assetId: string, isThumb?: boolean, isWeb?: boolean, options: AxiosRequestConfig = {}): Promise<RequestArgs> => { | ||||
|         downloadFile: async (assetId: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => { | ||||
|             // verify required parameter 'assetId' is not null or undefined
 | ||||
|             assertParamExists('downloadFile', 'assetId', assetId) | ||||
|             const localVarPath = `/asset/download/{assetId}` | ||||
| @@ -3754,14 +3752,6 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration | ||||
|             // http bearer authentication required
 | ||||
|             await setBearerAuthToObject(localVarHeaderParameter, configuration) | ||||
| 
 | ||||
|             if (isThumb !== undefined) { | ||||
|                 localVarQueryParameter['isThumb'] = isThumb; | ||||
|             } | ||||
| 
 | ||||
|             if (isWeb !== undefined) { | ||||
|                 localVarQueryParameter['isWeb'] = isWeb; | ||||
|             } | ||||
| 
 | ||||
| 
 | ||||
|      | ||||
|             setSearchParams(localVarUrlObj, localVarQueryParameter); | ||||
| @@ -4489,13 +4479,11 @@ export const AssetApiFp = function(configuration?: Configuration) { | ||||
|         /** | ||||
|          *  | ||||
|          * @param {string} assetId  | ||||
|          * @param {boolean} [isThumb]  | ||||
|          * @param {boolean} [isWeb]  | ||||
|          * @param {*} [options] Override http request option. | ||||
|          * @throws {RequiredError} | ||||
|          */ | ||||
|         async downloadFile(assetId: string, isThumb?: boolean, isWeb?: boolean, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<object>> { | ||||
|             const localVarAxiosArgs = await localVarAxiosParamCreator.downloadFile(assetId, isThumb, isWeb, options); | ||||
|         async downloadFile(assetId: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<object>> { | ||||
|             const localVarAxiosArgs = await localVarAxiosParamCreator.downloadFile(assetId, options); | ||||
|             return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); | ||||
|         }, | ||||
|         /** | ||||
| @@ -4719,13 +4707,11 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath | ||||
|         /** | ||||
|          *  | ||||
|          * @param {string} assetId  | ||||
|          * @param {boolean} [isThumb]  | ||||
|          * @param {boolean} [isWeb]  | ||||
|          * @param {*} [options] Override http request option. | ||||
|          * @throws {RequiredError} | ||||
|          */ | ||||
|         downloadFile(assetId: string, isThumb?: boolean, isWeb?: boolean, options?: any): AxiosPromise<object> { | ||||
|             return localVarFp.downloadFile(assetId, isThumb, isWeb, options).then((request) => request(axios, basePath)); | ||||
|         downloadFile(assetId: string, options?: any): AxiosPromise<object> { | ||||
|             return localVarFp.downloadFile(assetId, options).then((request) => request(axios, basePath)); | ||||
|         }, | ||||
|         /** | ||||
|          *  | ||||
| @@ -4939,14 +4925,12 @@ export class AssetApi extends BaseAPI { | ||||
|     /** | ||||
|      *  | ||||
|      * @param {string} assetId  | ||||
|      * @param {boolean} [isThumb]  | ||||
|      * @param {boolean} [isWeb]  | ||||
|      * @param {*} [options] Override http request option. | ||||
|      * @throws {RequiredError} | ||||
|      * @memberof AssetApi | ||||
|      */ | ||||
|     public downloadFile(assetId: string, isThumb?: boolean, isWeb?: boolean, options?: AxiosRequestConfig) { | ||||
|         return AssetApiFp(this.configuration).downloadFile(assetId, isThumb, isWeb, options).then((request) => request(this.axios, this.basePath)); | ||||
|     public downloadFile(assetId: string, options?: AxiosRequestConfig) { | ||||
|         return AssetApiFp(this.configuration).downloadFile(assetId, options).then((request) => request(this.axios, this.basePath)); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|   | ||||
							
								
								
									
										2
									
								
								web/src/api/open-api/base.ts
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2
									
								
								web/src/api/open-api/base.ts
									
									
									
										generated
									
									
									
								
							| @@ -4,7 +4,7 @@ | ||||
|  * Immich | ||||
|  * Immich API | ||||
|  * | ||||
|  * The version of the OpenAPI document: 1.43.0 | ||||
|  * The version of the OpenAPI document: 1.43.1 | ||||
|  *  | ||||
|  * | ||||
|  * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
 | ||||
|   | ||||
							
								
								
									
										2
									
								
								web/src/api/open-api/common.ts
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2
									
								
								web/src/api/open-api/common.ts
									
									
									
										generated
									
									
									
								
							| @@ -4,7 +4,7 @@ | ||||
|  * Immich | ||||
|  * Immich API | ||||
|  * | ||||
|  * The version of the OpenAPI document: 1.43.0 | ||||
|  * The version of the OpenAPI document: 1.43.1 | ||||
|  *  | ||||
|  * | ||||
|  * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
 | ||||
|   | ||||
							
								
								
									
										2
									
								
								web/src/api/open-api/configuration.ts
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2
									
								
								web/src/api/open-api/configuration.ts
									
									
									
										generated
									
									
									
								
							| @@ -4,7 +4,7 @@ | ||||
|  * Immich | ||||
|  * Immich API | ||||
|  * | ||||
|  * The version of the OpenAPI document: 1.43.0 | ||||
|  * The version of the OpenAPI document: 1.43.1 | ||||
|  *  | ||||
|  * | ||||
|  * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
 | ||||
|   | ||||
							
								
								
									
										2
									
								
								web/src/api/open-api/index.ts
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2
									
								
								web/src/api/open-api/index.ts
									
									
									
										generated
									
									
									
								
							| @@ -4,7 +4,7 @@ | ||||
|  * Immich | ||||
|  * Immich API | ||||
|  * | ||||
|  * The version of the OpenAPI document: 1.43.0 | ||||
|  * The version of the OpenAPI document: 1.43.1 | ||||
|  *  | ||||
|  * | ||||
|  * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
 | ||||
|   | ||||
| @@ -136,10 +136,8 @@ | ||||
|  | ||||
| 			$downloadAssets[imageFileName] = 0; | ||||
|  | ||||
| 			const { data, status } = await api.assetApi.downloadFile(assetId, false, false, { | ||||
| 				params: { | ||||
| 					key | ||||
| 				}, | ||||
| 			const { data, status } = await api.assetApi.downloadFile(assetId, { | ||||
| 				params: { key }, | ||||
| 				responseType: 'blob', | ||||
| 				onDownloadProgress: (progressEvent) => { | ||||
| 					if (progressEvent.lengthComputable) { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user