Forráskód Böngészése

chore(server) refactor serveFile and downloadFile endpoint (#978)

Alex 2 éve
szülő
commit
e799f35dd2

+ 1 - 2
mobile/lib/modules/asset_viewer/services/image_viewer.service.dart

@@ -23,8 +23,7 @@ class ImageViewerService {
       String fileName = p.basename(asset.originalPath);
 
       var res = await _apiService.assetApi.downloadFileWithHttpInfo(
-        asset.deviceAssetId,
-        asset.deviceId,
+        asset.id,
         isThumb: false,
         isWeb: false,
       );

+ 1 - 1
mobile/lib/modules/asset_viewer/views/video_viewer_page.dart

@@ -42,7 +42,7 @@ class VideoViewerPage extends HookConsumerWidget {
     final box = Hive.box(userInfoBox);
     final String jwtToken = box.get(accessTokenKey);
     final String videoUrl =
-        '${box.get(serverEndpointKey)}/asset/file?aid=${asset.deviceAssetId}&did=${asset.deviceId}';
+        '${box.get(serverEndpointKey)}/asset/file/${asset.id}';
 
     return Stack(
       children: [

+ 1 - 2
mobile/lib/shared/services/share.service.dart

@@ -28,8 +28,7 @@ class ShareService {
         final fileName = basename(asset.remote!.originalPath);
         final tempFile = await File('${tempDir.path}/$fileName').create();
         final res = await _apiService.assetApi.downloadFileWithHttpInfo(
-          asset.remote!.deviceAssetId,
-          asset.remote!.deviceId,
+          asset.remote!.id,
           isThumb: false,
           isWeb: false,
         );

+ 1 - 1
mobile/lib/utils/image_url_builder.dart

@@ -22,7 +22,7 @@ String getAlbumThumbnailUrl(
 
 String getImageUrl(final AssetResponseDto asset) {
   final box = Hive.box(userInfoBox);
-  return '${box.get(serverEndpointKey)}/asset/file?aid=${asset.deviceAssetId}&did=${asset.deviceId}&isThumb=false';
+  return '${box.get(serverEndpointKey)}/asset/file/${asset.id}?isThumb=false';
 }
 
 String _getThumbnailUrl(

+ 2 - 2
mobile/openapi/README.md

@@ -79,7 +79,7 @@ Class | Method | HTTP request | Description
 *AssetApi* | [**checkDuplicateAsset**](doc//AssetApi.md#checkduplicateasset) | **POST** /asset/check | 
 *AssetApi* | [**checkExistingAssets**](doc//AssetApi.md#checkexistingassets) | **POST** /asset/exist | 
 *AssetApi* | [**deleteAsset**](doc//AssetApi.md#deleteasset) | **DELETE** /asset | 
-*AssetApi* | [**downloadFile**](doc//AssetApi.md#downloadfile) | **GET** /asset/download | 
+*AssetApi* | [**downloadFile**](doc//AssetApi.md#downloadfile) | **GET** /asset/download/{assetId} | 
 *AssetApi* | [**downloadLibrary**](doc//AssetApi.md#downloadlibrary) | **GET** /asset/download-library | 
 *AssetApi* | [**getAllAssets**](doc//AssetApi.md#getallassets) | **GET** /asset | 
 *AssetApi* | [**getAssetById**](doc//AssetApi.md#getassetbyid) | **GET** /asset/assetById/{assetId} | 
@@ -92,7 +92,7 @@ Class | Method | HTTP request | Description
 *AssetApi* | [**getCuratedObjects**](doc//AssetApi.md#getcuratedobjects) | **GET** /asset/curated-objects | 
 *AssetApi* | [**getUserAssetsByDeviceId**](doc//AssetApi.md#getuserassetsbydeviceid) | **GET** /asset/{deviceId} | 
 *AssetApi* | [**searchAsset**](doc//AssetApi.md#searchasset) | **POST** /asset/search | 
-*AssetApi* | [**serveFile**](doc//AssetApi.md#servefile) | **GET** /asset/file | 
+*AssetApi* | [**serveFile**](doc//AssetApi.md#servefile) | **GET** /asset/file/{assetId} | 
 *AssetApi* | [**updateAssetById**](doc//AssetApi.md#updateassetbyid) | **PUT** /asset/assetById/{assetId} | 
 *AssetApi* | [**uploadFile**](doc//AssetApi.md#uploadfile) | **POST** /asset/upload | 
 *AuthenticationApi* | [**adminSignUp**](doc//AuthenticationApi.md#adminsignup) | **POST** /auth/admin-sign-up | 

+ 10 - 14
mobile/openapi/doc/AssetApi.md

@@ -12,7 +12,7 @@ Method | HTTP request | Description
 [**checkDuplicateAsset**](AssetApi.md#checkduplicateasset) | **POST** /asset/check | 
 [**checkExistingAssets**](AssetApi.md#checkexistingassets) | **POST** /asset/exist | 
 [**deleteAsset**](AssetApi.md#deleteasset) | **DELETE** /asset | 
-[**downloadFile**](AssetApi.md#downloadfile) | **GET** /asset/download | 
+[**downloadFile**](AssetApi.md#downloadfile) | **GET** /asset/download/{assetId} | 
 [**downloadLibrary**](AssetApi.md#downloadlibrary) | **GET** /asset/download-library | 
 [**getAllAssets**](AssetApi.md#getallassets) | **GET** /asset | 
 [**getAssetById**](AssetApi.md#getassetbyid) | **GET** /asset/assetById/{assetId} | 
@@ -25,7 +25,7 @@ Method | HTTP request | Description
 [**getCuratedObjects**](AssetApi.md#getcuratedobjects) | **GET** /asset/curated-objects | 
 [**getUserAssetsByDeviceId**](AssetApi.md#getuserassetsbydeviceid) | **GET** /asset/{deviceId} | 
 [**searchAsset**](AssetApi.md#searchasset) | **POST** /asset/search | 
-[**serveFile**](AssetApi.md#servefile) | **GET** /asset/file | 
+[**serveFile**](AssetApi.md#servefile) | **GET** /asset/file/{assetId} | 
 [**updateAssetById**](AssetApi.md#updateassetbyid) | **PUT** /asset/assetById/{assetId} | 
 [**uploadFile**](AssetApi.md#uploadfile) | **POST** /asset/upload | 
 
@@ -176,7 +176,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(aid, did, isThumb, isWeb)
+> Object downloadFile(assetId, isThumb, isWeb)
 
 
 
@@ -191,13 +191,12 @@ import 'package:openapi/api.dart';
 //defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
 
 final api_instance = AssetApi();
-final aid = aid_example; // String | 
-final did = did_example; // String | 
+final assetId = assetId_example; // String | 
 final isThumb = true; // bool | 
 final isWeb = true; // bool | 
 
 try {
-    final result = api_instance.downloadFile(aid, did, isThumb, isWeb);
+    final result = api_instance.downloadFile(assetId, isThumb, isWeb);
     print(result);
 } catch (e) {
     print('Exception when calling AssetApi->downloadFile: $e\n');
@@ -208,8 +207,7 @@ try {
 
 Name | Type | Description  | Notes
 ------------- | ------------- | ------------- | -------------
- **aid** | **String**|  | 
- **did** | **String**|  | 
+ **assetId** | **String**|  | 
  **isThumb** | **bool**|  | [optional] 
  **isWeb** | **bool**|  | [optional] 
 
@@ -781,7 +779,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)
 
 # **serveFile**
-> Object serveFile(aid, did, isThumb, isWeb)
+> Object serveFile(assetId, isThumb, isWeb)
 
 
 
@@ -796,13 +794,12 @@ import 'package:openapi/api.dart';
 //defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
 
 final api_instance = AssetApi();
-final aid = aid_example; // String | 
-final did = did_example; // String | 
+final assetId = assetId_example; // String | 
 final isThumb = true; // bool | 
 final isWeb = true; // bool | 
 
 try {
-    final result = api_instance.serveFile(aid, did, isThumb, isWeb);
+    final result = api_instance.serveFile(assetId, isThumb, isWeb);
     print(result);
 } catch (e) {
     print('Exception when calling AssetApi->serveFile: $e\n');
@@ -813,8 +810,7 @@ try {
 
 Name | Type | Description  | Notes
 ------------- | ------------- | ------------- | -------------
- **aid** | **String**|  | 
- **did** | **String**|  | 
+ **assetId** | **String**|  | 
  **isThumb** | **bool**|  | [optional] 
  **isWeb** | **bool**|  | [optional] 
 

+ 16 - 26
mobile/openapi/lib/api/asset_api.dart

@@ -178,19 +178,18 @@ class AssetApi {
     return null;
   }
 
-  /// Performs an HTTP 'GET /asset/download' operation and returns the [Response].
+  /// Performs an HTTP 'GET /asset/download/{assetId}' operation and returns the [Response].
   /// Parameters:
   ///
-  /// * [String] aid (required):
-  ///
-  /// * [String] did (required):
+  /// * [String] assetId (required):
   ///
   /// * [bool] isThumb:
   ///
   /// * [bool] isWeb:
-  Future<Response> downloadFileWithHttpInfo(String aid, String did, { bool? isThumb, bool? isWeb, }) async {
+  Future<Response> downloadFileWithHttpInfo(String assetId, { bool? isThumb, bool? isWeb, }) async {
     // ignore: prefer_const_declarations
-    final path = r'/asset/download';
+    final path = r'/asset/download/{assetId}'
+      .replaceAll('{assetId}', assetId);
 
     // ignore: prefer_final_locals
     Object? postBody;
@@ -199,8 +198,6 @@ class AssetApi {
     final headerParams = <String, String>{};
     final formParams = <String, String>{};
 
-      queryParams.addAll(_queryParams('', 'aid', aid));
-      queryParams.addAll(_queryParams('', 'did', did));
     if (isThumb != null) {
       queryParams.addAll(_queryParams('', 'isThumb', isThumb));
     }
@@ -224,15 +221,13 @@ class AssetApi {
 
   /// Parameters:
   ///
-  /// * [String] aid (required):
-  ///
-  /// * [String] did (required):
+  /// * [String] assetId (required):
   ///
   /// * [bool] isThumb:
   ///
   /// * [bool] isWeb:
-  Future<Object?> downloadFile(String aid, String did, { bool? isThumb, bool? isWeb, }) async {
-    final response = await downloadFileWithHttpInfo(aid, did,  isThumb: isThumb, isWeb: isWeb, );
+  Future<Object?> downloadFile(String assetId, { bool? isThumb, bool? isWeb, }) async {
+    final response = await downloadFileWithHttpInfo(assetId,  isThumb: isThumb, isWeb: isWeb, );
     if (response.statusCode >= HttpStatus.badRequest) {
       throw ApiException(response.statusCode, await _decodeBodyBytes(response));
     }
@@ -841,19 +836,18 @@ class AssetApi {
     return null;
   }
 
-  /// Performs an HTTP 'GET /asset/file' operation and returns the [Response].
+  /// Performs an HTTP 'GET /asset/file/{assetId}' operation and returns the [Response].
   /// Parameters:
   ///
-  /// * [String] aid (required):
-  ///
-  /// * [String] did (required):
+  /// * [String] assetId (required):
   ///
   /// * [bool] isThumb:
   ///
   /// * [bool] isWeb:
-  Future<Response> serveFileWithHttpInfo(String aid, String did, { bool? isThumb, bool? isWeb, }) async {
+  Future<Response> serveFileWithHttpInfo(String assetId, { bool? isThumb, bool? isWeb, }) async {
     // ignore: prefer_const_declarations
-    final path = r'/asset/file';
+    final path = r'/asset/file/{assetId}'
+      .replaceAll('{assetId}', assetId);
 
     // ignore: prefer_final_locals
     Object? postBody;
@@ -862,8 +856,6 @@ class AssetApi {
     final headerParams = <String, String>{};
     final formParams = <String, String>{};
 
-      queryParams.addAll(_queryParams('', 'aid', aid));
-      queryParams.addAll(_queryParams('', 'did', did));
     if (isThumb != null) {
       queryParams.addAll(_queryParams('', 'isThumb', isThumb));
     }
@@ -887,15 +879,13 @@ class AssetApi {
 
   /// Parameters:
   ///
-  /// * [String] aid (required):
-  ///
-  /// * [String] did (required):
+  /// * [String] assetId (required):
   ///
   /// * [bool] isThumb:
   ///
   /// * [bool] isWeb:
-  Future<Object?> serveFile(String aid, String did, { bool? isThumb, bool? isWeb, }) async {
-    final response = await serveFileWithHttpInfo(aid, did,  isThumb: isThumb, isWeb: isWeb, );
+  Future<Object?> serveFile(String assetId, { bool? isThumb, bool? isWeb, }) async {
+    final response = await serveFileWithHttpInfo(assetId,  isThumb: isThumb, isWeb: isWeb, );
     if (response.statusCode >= HttpStatus.badRequest) {
       throw ApiException(response.statusCode, await _decodeBodyBytes(response));
     }

+ 7 - 6
server/apps/immich/src/api-v1/asset/asset.controller.ts

@@ -131,13 +131,13 @@ export class AssetController {
     }
   }
 
-  @Get('/download')
+  @Get('/download/:assetId')
   async downloadFile(
-    @GetAuthUser() authUser: AuthUserDto,
     @Response({ passthrough: true }) res: Res,
     @Query(new ValidationPipe({ transform: true })) query: ServeFileDto,
+    @Param('assetId') assetId: string,
   ): Promise<any> {
-    return this.assetService.downloadFile(query, res);
+    return this.assetService.downloadFile(query, assetId, res);
   }
 
   @Get('/download-library')
@@ -154,14 +154,15 @@ export class AssetController {
     return stream;
   }
 
-  @Get('/file')
+  @Get('/file/:assetId')
+  @Header('Cache-Control', 'max-age=300')
   async serveFile(
     @Headers() headers: Record<string, string>,
-    @GetAuthUser() authUser: AuthUserDto,
     @Response({ passthrough: true }) res: Res,
     @Query(new ValidationPipe({ transform: true })) query: ServeFileDto,
+    @Param('assetId') assetId: string,
   ): Promise<any> {
-    return this.assetService.serveFile(authUser, query, res, headers);
+    return this.assetService.serveFile(assetId, query, res, headers);
   }
 
   @Get('/thumbnail/:assetId')

+ 4 - 20
server/apps/immich/src/api-v1/asset/asset.service.ts

@@ -107,22 +107,6 @@ export class AssetService {
     return assets.map((asset) => mapAsset(asset));
   }
 
-  // TODO - Refactor this to get asset by its own id
-  private async findAssetOfDevice(deviceId: string, assetId: string): Promise<AssetResponseDto> {
-    const rows = await this.assetRepository.query(
-      'SELECT * FROM assets a WHERE a."deviceAssetId" = $1 AND a."deviceId" = $2',
-      [assetId, deviceId],
-    );
-
-    if (rows.lengh == 0) {
-      throw new NotFoundException('Not Found');
-    }
-
-    const assetOnDevice = rows[0] as AssetEntity;
-
-    return mapAsset(assetOnDevice);
-  }
-
   public async getAssetById(authUser: AuthUserDto, assetId: string): Promise<AssetResponseDto> {
     const asset = await this._assetRepository.getById(assetId);
 
@@ -150,10 +134,10 @@ export class AssetService {
     return this.downloadService.downloadArchive(dto.name || `library`, assets);
   }
 
-  public async downloadFile(query: ServeFileDto, res: Res) {
+  public async downloadFile(query: ServeFileDto, assetId: string, res: Res) {
     try {
       let fileReadStream = null;
-      const asset = await this.findAssetOfDevice(query.did, query.aid);
+      const asset = await this._assetRepository.getById(assetId);
 
       // Download Video
       if (asset.type === AssetType.VIDEO) {
@@ -251,9 +235,9 @@ export class AssetService {
     }
   }
 
-  public async serveFile(authUser: AuthUserDto, query: ServeFileDto, res: Res, headers: any) {
+  public async serveFile(assetId: string, query: ServeFileDto, res: Res, headers: any) {
     let fileReadStream: ReadStream;
-    const asset = await this.findAssetOfDevice(query.did, query.aid);
+    const asset = await this._assetRepository.getById(assetId);
 
     if (!asset) {
       throw new NotFoundException('Asset does not exist');

+ 1 - 9
server/apps/immich/src/api-v1/asset/dto/serve-file.dto.ts

@@ -1,16 +1,8 @@
 import { ApiProperty } from '@nestjs/swagger';
 import { Transform } from 'class-transformer';
-import { IsBoolean, IsNotEmpty, IsOptional } from 'class-validator';
+import { IsBoolean, IsOptional } from 'class-validator';
 
 export class ServeFileDto {
-  @IsNotEmpty()
-  @ApiProperty({ type: String, title: 'Device Asset ID' })
-  aid!: string;
-
-  @IsNotEmpty()
-  @ApiProperty({ type: String, title: 'Device ID' })
-  did!: string;
-
   @IsOptional()
   @IsBoolean()
   @Transform(({ value }) => {

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 0 - 0
server/immich-openapi-specs.json


+ 22 - 2
server/package-lock.json

@@ -33,6 +33,7 @@
         "diskusage": "^1.1.3",
         "dotenv": "^14.2.0",
         "exifr": "^7.1.3",
+        "fdir": "^5.3.0",
         "fluent-ffmpeg": "^2.1.2",
         "geo-tz": "^7.0.2",
         "i18n-iso-countries": "^7.5.0",
@@ -5583,6 +5584,19 @@
         "bser": "2.1.1"
       }
     },
+    "node_modules/fdir": {
+      "version": "5.3.0",
+      "resolved": "https://registry.npmjs.org/fdir/-/fdir-5.3.0.tgz",
+      "integrity": "sha512-BtE53+jaa7nNHT+gPdfU6cFAXOJUWDs2b5GFox8dtl6zLXmfNf/N6im69b9nqNNwDyl27mpIWX8qR7AafWzSdQ==",
+      "peerDependencies": {
+        "picomatch": "2.x"
+      },
+      "peerDependenciesMeta": {
+        "picomatch": {
+          "optional": true
+        }
+      }
+    },
     "node_modules/figures": {
       "version": "3.2.0",
       "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz",
@@ -8885,7 +8899,7 @@
       "version": "2.3.1",
       "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
       "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
-      "dev": true,
+      "devOptional": true,
       "engines": {
         "node": ">=8.6"
       },
@@ -15772,6 +15786,12 @@
         "bser": "2.1.1"
       }
     },
+    "fdir": {
+      "version": "5.3.0",
+      "resolved": "https://registry.npmjs.org/fdir/-/fdir-5.3.0.tgz",
+      "integrity": "sha512-BtE53+jaa7nNHT+gPdfU6cFAXOJUWDs2b5GFox8dtl6zLXmfNf/N6im69b9nqNNwDyl27mpIWX8qR7AafWzSdQ==",
+      "requires": {}
+    },
     "figures": {
       "version": "3.2.0",
       "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz",
@@ -18309,7 +18329,7 @@
       "version": "2.3.1",
       "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
       "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
-      "dev": true
+      "devOptional": true
     },
     "pirates": {
       "version": "4.0.5",

+ 3 - 2
server/package.json

@@ -56,6 +56,7 @@
     "diskusage": "^1.1.3",
     "dotenv": "^14.2.0",
     "exifr": "^7.1.3",
+    "fdir": "^5.3.0",
     "fluent-ffmpeg": "^2.1.2",
     "geo-tz": "^7.0.2",
     "i18n-iso-countries": "^7.5.0",
@@ -63,8 +64,8 @@
     "local-reverse-geocoder": "^0.12.5",
     "lodash": "^4.17.21",
     "luxon": "^3.0.3",
-    "openid-client": "^5.2.1",
     "nest-commander": "^3.3.0",
+    "openid-client": "^5.2.1",
     "passport": "^0.6.0",
     "passport-jwt": "^4.0.0",
     "pg": "^8.7.1",
@@ -144,4 +145,4 @@
       "^@app/system-config(|/.*)$": "<rootDir>/libs/system-config/src/$1"
     }
   }
-}
+}

+ 30 - 56
web/src/api/open-api/api.ts

@@ -2702,19 +2702,17 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration
         },
         /**
          * 
-         * @param {string} aid 
-         * @param {string} did 
+         * @param {string} assetId 
          * @param {boolean} [isThumb] 
          * @param {boolean} [isWeb] 
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
          */
-        downloadFile: async (aid: string, did: string, isThumb?: boolean, isWeb?: boolean, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
-            // verify required parameter 'aid' is not null or undefined
-            assertParamExists('downloadFile', 'aid', aid)
-            // verify required parameter 'did' is not null or undefined
-            assertParamExists('downloadFile', 'did', did)
-            const localVarPath = `/asset/download`;
+        downloadFile: async (assetId: string, isThumb?: boolean, isWeb?: boolean, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
+            // verify required parameter 'assetId' is not null or undefined
+            assertParamExists('downloadFile', 'assetId', assetId)
+            const localVarPath = `/asset/download/{assetId}`
+                .replace(`{${"assetId"}}`, encodeURIComponent(String(assetId)));
             // use dummy base URL string because the URL constructor only accepts absolute URLs.
             const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
             let baseOptions;
@@ -2730,14 +2728,6 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration
             // http bearer authentication required
             await setBearerAuthToObject(localVarHeaderParameter, configuration)
 
-            if (aid !== undefined) {
-                localVarQueryParameter['aid'] = aid;
-            }
-
-            if (did !== undefined) {
-                localVarQueryParameter['did'] = did;
-            }
-
             if (isThumb !== undefined) {
                 localVarQueryParameter['isThumb'] = isThumb;
             }
@@ -3198,19 +3188,17 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration
         },
         /**
          * 
-         * @param {string} aid 
-         * @param {string} did 
+         * @param {string} assetId 
          * @param {boolean} [isThumb] 
          * @param {boolean} [isWeb] 
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
          */
-        serveFile: async (aid: string, did: string, isThumb?: boolean, isWeb?: boolean, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
-            // verify required parameter 'aid' is not null or undefined
-            assertParamExists('serveFile', 'aid', aid)
-            // verify required parameter 'did' is not null or undefined
-            assertParamExists('serveFile', 'did', did)
-            const localVarPath = `/asset/file`;
+        serveFile: async (assetId: string, isThumb?: boolean, isWeb?: boolean, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
+            // verify required parameter 'assetId' is not null or undefined
+            assertParamExists('serveFile', 'assetId', assetId)
+            const localVarPath = `/asset/file/{assetId}`
+                .replace(`{${"assetId"}}`, encodeURIComponent(String(assetId)));
             // use dummy base URL string because the URL constructor only accepts absolute URLs.
             const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
             let baseOptions;
@@ -3226,14 +3214,6 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration
             // http bearer authentication required
             await setBearerAuthToObject(localVarHeaderParameter, configuration)
 
-            if (aid !== undefined) {
-                localVarQueryParameter['aid'] = aid;
-            }
-
-            if (did !== undefined) {
-                localVarQueryParameter['did'] = did;
-            }
-
             if (isThumb !== undefined) {
                 localVarQueryParameter['isThumb'] = isThumb;
             }
@@ -3385,15 +3365,14 @@ export const AssetApiFp = function(configuration?: Configuration) {
         },
         /**
          * 
-         * @param {string} aid 
-         * @param {string} did 
+         * @param {string} assetId 
          * @param {boolean} [isThumb] 
          * @param {boolean} [isWeb] 
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
          */
-        async downloadFile(aid: string, did: string, isThumb?: boolean, isWeb?: boolean, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<object>> {
-            const localVarAxiosArgs = await localVarAxiosParamCreator.downloadFile(aid, did, isThumb, isWeb, options);
+        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);
             return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
         },
         /**
@@ -3517,15 +3496,14 @@ export const AssetApiFp = function(configuration?: Configuration) {
         },
         /**
          * 
-         * @param {string} aid 
-         * @param {string} did 
+         * @param {string} assetId 
          * @param {boolean} [isThumb] 
          * @param {boolean} [isWeb] 
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
          */
-        async serveFile(aid: string, did: string, isThumb?: boolean, isWeb?: boolean, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<object>> {
-            const localVarAxiosArgs = await localVarAxiosParamCreator.serveFile(aid, did, isThumb, isWeb, options);
+        async serveFile(assetId: string, isThumb?: boolean, isWeb?: boolean, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<object>> {
+            const localVarAxiosArgs = await localVarAxiosParamCreator.serveFile(assetId, isThumb, isWeb, options);
             return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
         },
         /**
@@ -3591,15 +3569,14 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath
         },
         /**
          * 
-         * @param {string} aid 
-         * @param {string} did 
+         * @param {string} assetId 
          * @param {boolean} [isThumb] 
          * @param {boolean} [isWeb] 
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
          */
-        downloadFile(aid: string, did: string, isThumb?: boolean, isWeb?: boolean, options?: any): AxiosPromise<object> {
-            return localVarFp.downloadFile(aid, did, isThumb, isWeb, options).then((request) => request(axios, basePath));
+        downloadFile(assetId: string, isThumb?: boolean, isWeb?: boolean, options?: any): AxiosPromise<object> {
+            return localVarFp.downloadFile(assetId, isThumb, isWeb, options).then((request) => request(axios, basePath));
         },
         /**
          * 
@@ -3710,15 +3687,14 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath
         },
         /**
          * 
-         * @param {string} aid 
-         * @param {string} did 
+         * @param {string} assetId 
          * @param {boolean} [isThumb] 
          * @param {boolean} [isWeb] 
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
          */
-        serveFile(aid: string, did: string, isThumb?: boolean, isWeb?: boolean, options?: any): AxiosPromise<object> {
-            return localVarFp.serveFile(aid, did, isThumb, isWeb, options).then((request) => request(axios, basePath));
+        serveFile(assetId: string, isThumb?: boolean, isWeb?: boolean, options?: any): AxiosPromise<object> {
+            return localVarFp.serveFile(assetId, isThumb, isWeb, options).then((request) => request(axios, basePath));
         },
         /**
          * Update an asset
@@ -3787,16 +3763,15 @@ export class AssetApi extends BaseAPI {
 
     /**
      * 
-     * @param {string} aid 
-     * @param {string} did 
+     * @param {string} assetId 
      * @param {boolean} [isThumb] 
      * @param {boolean} [isWeb] 
      * @param {*} [options] Override http request option.
      * @throws {RequiredError}
      * @memberof AssetApi
      */
-    public downloadFile(aid: string, did: string, isThumb?: boolean, isWeb?: boolean, options?: AxiosRequestConfig) {
-        return AssetApiFp(this.configuration).downloadFile(aid, did, isThumb, isWeb, options).then((request) => request(this.axios, this.basePath));
+    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));
     }
 
     /**
@@ -3932,16 +3907,15 @@ export class AssetApi extends BaseAPI {
 
     /**
      * 
-     * @param {string} aid 
-     * @param {string} did 
+     * @param {string} assetId 
      * @param {boolean} [isThumb] 
      * @param {boolean} [isWeb] 
      * @param {*} [options] Override http request option.
      * @throws {RequiredError}
      * @memberof AssetApi
      */
-    public serveFile(aid: string, did: string, isThumb?: boolean, isWeb?: boolean, options?: AxiosRequestConfig) {
-        return AssetApiFp(this.configuration).serveFile(aid, did, isThumb, isWeb, options).then((request) => request(this.axios, this.basePath));
+    public serveFile(assetId: string, isThumb?: boolean, isWeb?: boolean, options?: AxiosRequestConfig) {
+        return AssetApiFp(this.configuration).serveFile(assetId, isThumb, isWeb, options).then((request) => request(this.axios, this.basePath));
     }
 
     /**

+ 2 - 4
web/src/api/utils.ts

@@ -1,10 +1,8 @@
 const _basePath = '/api';
 
-export function getFileUrl(aid: string, did: string, isThumb?: boolean, isWeb?: boolean) {
-	const urlObj = new URL(`${window.location.origin}${_basePath}/asset/file`);
+export function getFileUrl(assetId: string, isThumb?: boolean, isWeb?: boolean) {
+	const urlObj = new URL(`${window.location.origin}${_basePath}/asset/file/${assetId}`);
 
-	urlObj.searchParams.append('aid', aid);
-	urlObj.searchParams.append('did', did);
 	if (isThumb !== undefined && isThumb !== null)
 		urlObj.searchParams.append('isThumb', `${isThumb}`);
 	if (isWeb !== undefined && isWeb !== null) urlObj.searchParams.append('isWeb', `${isWeb}`);

+ 9 - 15
web/src/lib/components/asset-viewer/asset-viewer.svelte

@@ -101,22 +101,16 @@
 
 			$downloadAssets[imageFileName] = 0;
 
-			const { data, status } = await api.assetApi.downloadFile(
-				asset.deviceAssetId,
-				asset.deviceId,
-				false,
-				false,
-				{
-					responseType: 'blob',
-					onDownloadProgress: (progressEvent) => {
-						if (progressEvent.lengthComputable) {
-							const total = progressEvent.total;
-							const current = progressEvent.loaded;
-							$downloadAssets[imageFileName] = Math.floor((current / total) * 100);
-						}
+			const { data, status } = await api.assetApi.downloadFile(asset.id, false, false, {
+				responseType: 'blob',
+				onDownloadProgress: (progressEvent) => {
+					if (progressEvent.lengthComputable) {
+						const total = progressEvent.total;
+						const current = progressEvent.loaded;
+						$downloadAssets[imageFileName] = Math.floor((current / total) * 100);
 					}
 				}
-			);
+			});
 
 			if (!(data instanceof Blob)) {
 				return;
@@ -262,7 +256,7 @@
 	<div class="row-start-1 row-span-full col-start-1 col-span-4">
 		{#key asset.id}
 			{#if asset.type === AssetTypeEnum.Image}
-				<PhotoViewer assetId={asset.id} deviceId={asset.deviceId} on:close={closeViewer} />
+				<PhotoViewer assetId={asset.id} on:close={closeViewer} />
 			{:else}
 				<VideoViewer assetId={asset.id} on:close={closeViewer} />
 			{/if}

+ 3 - 10
web/src/lib/components/asset-viewer/photo-viewer.svelte

@@ -7,7 +7,6 @@
 	import Keydown from 'svelte-keydown';
 
 	export let assetId: string;
-	export let deviceId: string;
 
 	let assetInfo: AssetResponseDto;
 	let assetData: string;
@@ -25,15 +24,9 @@
 
 	const loadAssetData = async () => {
 		try {
-			const { data } = await api.assetApi.serveFile(
-				assetInfo.deviceAssetId,
-				deviceId,
-				false,
-				true,
-				{
-					responseType: 'blob'
-				}
-			);
+			const { data } = await api.assetApi.serveFile(assetInfo.id, false, true, {
+				responseType: 'blob'
+			});
 
 			if (!(data instanceof Blob)) {
 				return;

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

@@ -24,7 +24,7 @@
 	const loadVideoData = async (assetInfo: AssetResponseDto) => {
 		isVideoLoading = true;
 
-		videoUrl = getFileUrl(assetInfo.deviceAssetId, assetInfo.deviceId, false, true);
+		videoUrl = getFileUrl(assetInfo.id, false, true);
 
 		return assetInfo;
 	};

+ 1 - 1
web/src/lib/components/shared-components/immich-thumbnail.svelte

@@ -31,7 +31,7 @@
 	const loadVideoData = async () => {
 		isThumbnailVideoPlaying = false;
 
-		videoUrl = getFileUrl(asset.deviceAssetId, asset.deviceId, false, true);
+		videoUrl = getFileUrl(asset.id, false, true);
 	};
 
 	const getVideoDurationInString = (currentTime: number) => {

Nem az összes módosított fájl került megjelenítésre, mert túl sok fájl változott