diff --git a/mobile/openapi/doc/AlbumApi.md b/mobile/openapi/doc/AlbumApi.md index 72cb3392e..d71f4e297 100644 Binary files a/mobile/openapi/doc/AlbumApi.md and b/mobile/openapi/doc/AlbumApi.md differ diff --git a/mobile/openapi/doc/AssetApi.md b/mobile/openapi/doc/AssetApi.md index 53208634b..086ead94c 100644 Binary files a/mobile/openapi/doc/AssetApi.md and b/mobile/openapi/doc/AssetApi.md differ diff --git a/mobile/openapi/doc/CreateAlbumShareLinkDto.md b/mobile/openapi/doc/CreateAlbumShareLinkDto.md index a0010d761..522314932 100644 Binary files a/mobile/openapi/doc/CreateAlbumShareLinkDto.md and b/mobile/openapi/doc/CreateAlbumShareLinkDto.md differ diff --git a/mobile/openapi/lib/api/album_api.dart b/mobile/openapi/lib/api/album_api.dart index 8f42545b8..8f7fe5ab3 100644 Binary files a/mobile/openapi/lib/api/album_api.dart and b/mobile/openapi/lib/api/album_api.dart differ diff --git a/mobile/openapi/lib/api/asset_api.dart b/mobile/openapi/lib/api/asset_api.dart index c851b943d..ea05bf844 100644 Binary files a/mobile/openapi/lib/api/asset_api.dart and b/mobile/openapi/lib/api/asset_api.dart differ diff --git a/mobile/openapi/lib/model/create_album_share_link_dto.dart b/mobile/openapi/lib/model/create_album_share_link_dto.dart index 46b3812a9..43aadbe05 100644 Binary files a/mobile/openapi/lib/model/create_album_share_link_dto.dart and b/mobile/openapi/lib/model/create_album_share_link_dto.dart differ diff --git a/mobile/openapi/test/album_api_test.dart b/mobile/openapi/test/album_api_test.dart index eb6f87395..fbb2e166c 100644 Binary files a/mobile/openapi/test/album_api_test.dart and b/mobile/openapi/test/album_api_test.dart differ diff --git a/mobile/openapi/test/asset_api_test.dart b/mobile/openapi/test/asset_api_test.dart index cf428cb18..3430b2e03 100644 Binary files a/mobile/openapi/test/asset_api_test.dart and b/mobile/openapi/test/asset_api_test.dart differ diff --git a/mobile/openapi/test/create_album_share_link_dto_test.dart b/mobile/openapi/test/create_album_share_link_dto_test.dart index 7b44683c5..c9dfc6376 100644 Binary files a/mobile/openapi/test/create_album_share_link_dto_test.dart and b/mobile/openapi/test/create_album_share_link_dto_test.dart differ diff --git a/server/apps/immich/src/api-v1/album/album.controller.ts b/server/apps/immich/src/api-v1/album/album.controller.ts index d204f44af..fff93f140 100644 --- a/server/apps/immich/src/api-v1/album/album.controller.ts +++ b/server/apps/immich/src/api-v1/album/album.controller.ts @@ -1,16 +1,4 @@ -import { - Controller, - Get, - Post, - Body, - Patch, - Param, - Delete, - ValidationPipe, - Put, - Query, - Response, -} from '@nestjs/common'; +import { Controller, Get, Post, Body, Patch, Param, Delete, Put, Query, Response } from '@nestjs/common'; import { ParseMeUUIDPipe } from '../validation/parse-me-uuid-pipe'; import { AlbumService } from './album.service'; import { CreateAlbumDto } from './dto/create-album.dto'; @@ -33,9 +21,11 @@ import { import { DownloadDto } from '../asset/dto/download-library.dto'; import { CreateAlbumShareLinkDto as CreateAlbumSharedLinkDto } from './dto/create-album-shared-link.dto'; import { AlbumIdDto } from './dto/album-id.dto'; +import { UseValidation } from '../../decorators/use-validation.decorator'; @ApiTags('Album') @Controller('album') +@UseValidation() export class AlbumController { constructor(private readonly albumService: AlbumService) {} @@ -47,7 +37,8 @@ export class AlbumController { @Authenticated() @Post() - async createAlbum(@GetAuthUser() authUser: AuthUserDto, @Body(ValidationPipe) createAlbumDto: CreateAlbumDto) { + async createAlbum(@GetAuthUser() authUser: AuthUserDto, @Body() createAlbumDto: CreateAlbumDto) { + // TODO: Handle nonexistent sharedWithUserIds and assetIds. return this.albumService.create(authUser, createAlbumDto); } @@ -55,9 +46,10 @@ export class AlbumController { @Put('/:albumId/users') async addUsersToAlbum( @GetAuthUser() authUser: AuthUserDto, - @Body(ValidationPipe) addUsersDto: AddUsersDto, + @Body() addUsersDto: AddUsersDto, @Param() { albumId }: AlbumIdDto, ) { + // TODO: Handle nonexistent sharedUserIds. return this.albumService.addUsersToAlbum(authUser, addUsersDto, albumId); } @@ -65,9 +57,11 @@ export class AlbumController { @Put('/:albumId/assets') async addAssetsToAlbum( @GetAuthUser() authUser: AuthUserDto, - @Body(ValidationPipe) addAssetsDto: AddAssetsDto, + @Body() addAssetsDto: AddAssetsDto, @Param() { albumId }: AlbumIdDto, ): Promise { + // TODO: Handle nonexistent assetIds. + // TODO: Disallow adding assets of another user to an album. return this.albumService.addAssetsToAlbum(authUser, addAssetsDto, albumId); } @@ -81,7 +75,7 @@ export class AlbumController { @Delete('/:albumId/assets') async removeAssetFromAlbum( @GetAuthUser() authUser: AuthUserDto, - @Body(ValidationPipe) removeAssetsDto: RemoveAssetsDto, + @Body() removeAssetsDto: RemoveAssetsDto, @Param() { albumId }: AlbumIdDto, ): Promise { return this.albumService.removeAssetsFromAlbum(authUser, removeAssetsDto, albumId); @@ -107,9 +101,11 @@ export class AlbumController { @Patch('/:albumId') async updateAlbumInfo( @GetAuthUser() authUser: AuthUserDto, - @Body(ValidationPipe) updateAlbumInfoDto: UpdateAlbumDto, + @Body() updateAlbumInfoDto: UpdateAlbumDto, @Param() { albumId }: AlbumIdDto, ) { + // TODO: Handle nonexistent albumThumbnailAssetId. + // TODO: Disallow setting asset from other user as albumThumbnailAssetId. return this.albumService.updateAlbumInfo(authUser, updateAlbumInfoDto, albumId); } @@ -119,7 +115,7 @@ export class AlbumController { async downloadArchive( @GetAuthUser() authUser: AuthUserDto, @Param() { albumId }: AlbumIdDto, - @Query(new ValidationPipe({ transform: true })) dto: DownloadDto, + @Query() dto: DownloadDto, @Response({ passthrough: true }) res: Res, ) { this.albumService.checkDownloadAccess(authUser); @@ -140,7 +136,7 @@ export class AlbumController { @Post('/create-shared-link') async createAlbumSharedLink( @GetAuthUser() authUser: AuthUserDto, - @Body(ValidationPipe) createAlbumShareLinkDto: CreateAlbumSharedLinkDto, + @Body() createAlbumShareLinkDto: CreateAlbumSharedLinkDto, ) { return this.albumService.createAlbumSharedLink(authUser, createAlbumShareLinkDto); } diff --git a/server/apps/immich/src/api-v1/album/dto/add-assets.dto.ts b/server/apps/immich/src/api-v1/album/dto/add-assets.dto.ts index 18b29f7c8..bf0d53135 100644 --- a/server/apps/immich/src/api-v1/album/dto/add-assets.dto.ts +++ b/server/apps/immich/src/api-v1/album/dto/add-assets.dto.ts @@ -1,6 +1,6 @@ -import { IsNotEmpty } from 'class-validator'; +import { ValidateUUID } from 'apps/immich/src/decorators/validate-uuid.decorator'; export class AddAssetsDto { - @IsNotEmpty() + @ValidateUUID({ each: true }) assetIds!: string[]; } diff --git a/server/apps/immich/src/api-v1/album/dto/add-users.dto.ts b/server/apps/immich/src/api-v1/album/dto/add-users.dto.ts index 6a386053a..c4f3c677f 100644 --- a/server/apps/immich/src/api-v1/album/dto/add-users.dto.ts +++ b/server/apps/immich/src/api-v1/album/dto/add-users.dto.ts @@ -1,6 +1,6 @@ -import { IsNotEmpty } from 'class-validator'; +import { ValidateUUID } from 'apps/immich/src/decorators/validate-uuid.decorator'; export class AddUsersDto { - @IsNotEmpty() + @ValidateUUID({ each: true }) sharedUserIds!: string[]; } diff --git a/server/apps/immich/src/api-v1/album/dto/album-id.dto.ts b/server/apps/immich/src/api-v1/album/dto/album-id.dto.ts index c9bf09e32..5dbdf06c2 100644 --- a/server/apps/immich/src/api-v1/album/dto/album-id.dto.ts +++ b/server/apps/immich/src/api-v1/album/dto/album-id.dto.ts @@ -1,9 +1,6 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { IsNotEmpty, IsUUID } from 'class-validator'; +import { ValidateUUID } from 'apps/immich/src/decorators/validate-uuid.decorator'; export class AlbumIdDto { - @IsNotEmpty() - @IsUUID('4') - @ApiProperty({ format: 'uuid' }) + @ValidateUUID() albumId!: string; } diff --git a/server/apps/immich/src/api-v1/album/dto/create-album-shared-link.dto.ts b/server/apps/immich/src/api-v1/album/dto/create-album-shared-link.dto.ts index e113b9d49..a35e52748 100644 --- a/server/apps/immich/src/api-v1/album/dto/create-album-shared-link.dto.ts +++ b/server/apps/immich/src/api-v1/album/dto/create-album-shared-link.dto.ts @@ -1,27 +1,33 @@ -import { IsBoolean, IsNotEmpty, IsOptional, IsString } from 'class-validator'; +import { ApiProperty } from '@nestjs/swagger'; +import { ValidateUUID } from 'apps/immich/src/decorators/validate-uuid.decorator'; +import { IsBoolean, IsISO8601, IsOptional, IsString } from 'class-validator'; export class CreateAlbumShareLinkDto { - @IsString() - @IsNotEmpty() + @ValidateUUID() albumId!: string; - @IsString() + @IsISO8601() @IsOptional() + @ApiProperty({ format: 'date-time' }) expiresAt?: string; @IsBoolean() @IsOptional() + @ApiProperty() allowUpload?: boolean; @IsBoolean() @IsOptional() + @ApiProperty() allowDownload?: boolean; @IsBoolean() @IsOptional() + @ApiProperty() showExif?: boolean; @IsString() @IsOptional() + @ApiProperty() description?: string; } diff --git a/server/apps/immich/src/api-v1/album/dto/create-album.dto.ts b/server/apps/immich/src/api-v1/album/dto/create-album.dto.ts index f6b1e9476..9857c26a3 100644 --- a/server/apps/immich/src/api-v1/album/dto/create-album.dto.ts +++ b/server/apps/immich/src/api-v1/album/dto/create-album.dto.ts @@ -1,12 +1,16 @@ -import { IsNotEmpty, IsOptional } from 'class-validator'; +import { ApiProperty } from '@nestjs/swagger'; +import { ValidateUUID } from 'apps/immich/src/decorators/validate-uuid.decorator'; +import { IsNotEmpty, IsString } from 'class-validator'; export class CreateAlbumDto { @IsNotEmpty() + @IsString() + @ApiProperty() albumName!: string; - @IsOptional() + @ValidateUUID({ optional: true, each: true }) sharedWithUserIds?: string[]; - @IsOptional() + @ValidateUUID({ optional: true, each: true }) assetIds?: string[]; } diff --git a/server/apps/immich/src/api-v1/album/dto/remove-assets.dto.ts b/server/apps/immich/src/api-v1/album/dto/remove-assets.dto.ts index 73f2994d9..d1b2651ec 100644 --- a/server/apps/immich/src/api-v1/album/dto/remove-assets.dto.ts +++ b/server/apps/immich/src/api-v1/album/dto/remove-assets.dto.ts @@ -1,6 +1,6 @@ -import { IsNotEmpty } from 'class-validator'; +import { ValidateUUID } from 'apps/immich/src/decorators/validate-uuid.decorator'; export class RemoveAssetsDto { - @IsNotEmpty() + @ValidateUUID({ each: true }) assetIds!: string[]; } diff --git a/server/apps/immich/src/api-v1/album/dto/update-album.dto.ts b/server/apps/immich/src/api-v1/album/dto/update-album.dto.ts index 44b0764d5..50853cd5a 100644 --- a/server/apps/immich/src/api-v1/album/dto/update-album.dto.ts +++ b/server/apps/immich/src/api-v1/album/dto/update-album.dto.ts @@ -1,9 +1,12 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { ValidateUUID } from 'apps/immich/src/decorators/validate-uuid.decorator'; import { IsOptional } from 'class-validator'; export class UpdateAlbumDto { @IsOptional() + @ApiProperty() albumName?: string; - @IsOptional() + @ValidateUUID({ optional: true }) albumThumbnailAssetId?: string; } diff --git a/server/apps/immich/src/api-v1/asset/dto/download-library.dto.ts b/server/apps/immich/src/api-v1/asset/dto/download-library.dto.ts index fdfeca1be..7e1dfd12d 100644 --- a/server/apps/immich/src/api-v1/asset/dto/download-library.dto.ts +++ b/server/apps/immich/src/api-v1/asset/dto/download-library.dto.ts @@ -4,7 +4,7 @@ import { IsNumber, IsOptional, IsPositive, IsString } from 'class-validator'; export class DownloadDto { @IsOptional() @IsString() - name = ''; + name?: string; @IsOptional() @IsPositive() diff --git a/server/apps/immich/src/decorators/validate-uuid.decorator.ts b/server/apps/immich/src/decorators/validate-uuid.decorator.ts new file mode 100644 index 000000000..16a2e1ad0 --- /dev/null +++ b/server/apps/immich/src/decorators/validate-uuid.decorator.ts @@ -0,0 +1,17 @@ +import { applyDecorators } from '@nestjs/common'; +import { ApiProperty } from '@nestjs/swagger'; +import { IsArray, IsNotEmpty, IsOptional, IsString, IsUUID } from 'class-validator'; + +export type Options = { + optional?: boolean; + each?: boolean; +}; + +export function ValidateUUID({ optional, each }: Options = { optional: false, each: false }) { + return applyDecorators( + IsUUID('4', { each }), + ApiProperty({ format: 'uuid' }), + optional ? IsOptional() : IsNotEmpty(), + each ? IsArray() : IsString(), + ); +} diff --git a/server/immich-openapi-specs.json b/server/immich-openapi-specs.json index 8111eae2b..7318739e2 100644 --- a/server/immich-openapi-specs.json +++ b/server/immich-openapi-specs.json @@ -1895,6 +1895,14 @@ "operationId": "downloadLibrary", "description": "Current this is not used in any UI element", "parameters": [ + { + "name": "name", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + }, { "name": "skip", "required": false, @@ -3343,6 +3351,14 @@ "type": "string" } }, + { + "name": "name", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + }, { "name": "skip", "required": false, @@ -5359,7 +5375,8 @@ "assetIds": { "type": "array", "items": { - "type": "string" + "type": "string", + "format": "uuid" } } }, @@ -5373,7 +5390,8 @@ "assetIds": { "type": "array", "items": { - "type": "string" + "type": "string", + "format": "uuid" } } }, @@ -5435,13 +5453,15 @@ "sharedWithUserIds": { "type": "array", "items": { - "type": "string" + "type": "string", + "format": "uuid" } }, "assetIds": { "type": "array", "items": { - "type": "string" + "type": "string", + "format": "uuid" } } }, @@ -5455,7 +5475,8 @@ "sharedUserIds": { "type": "array", "items": { - "type": "string" + "type": "string", + "format": "uuid" } } }, @@ -5491,7 +5512,8 @@ "type": "string" }, "albumThumbnailAssetId": { - "type": "string" + "type": "string", + "format": "uuid" } } }, @@ -5499,10 +5521,12 @@ "type": "object", "properties": { "albumId": { - "type": "string" + "type": "string", + "format": "uuid" }, "expiresAt": { - "type": "string" + "type": "string", + "format": "date-time" }, "allowUpload": { "type": "boolean" diff --git a/server/libs/domain/src/album/dto/get-albums.dto.ts b/server/libs/domain/src/album/dto/get-albums.dto.ts index afcde1f12..ea62ffc20 100644 --- a/server/libs/domain/src/album/dto/get-albums.dto.ts +++ b/server/libs/domain/src/album/dto/get-albums.dto.ts @@ -1,7 +1,8 @@ import { Transform } from 'class-transformer'; -import { IsBoolean, IsOptional, IsUUID } from 'class-validator'; +import { IsBoolean, IsOptional } from 'class-validator'; import { toBoolean } from 'apps/immich/src/utils/transform.util'; import { ApiProperty } from '@nestjs/swagger'; +import { ValidateUUID } from 'apps/immich/src/decorators/validate-uuid.decorator'; export class GetAlbumsDto { @IsOptional() @@ -20,8 +21,6 @@ export class GetAlbumsDto { * Ignores the shared parameter * undefined: get all albums */ - @IsOptional() - @IsUUID(4) - @ApiProperty({ format: 'uuid' }) + @ValidateUUID({ optional: true }) assetId?: string; } diff --git a/web/src/api/open-api/api.ts b/web/src/api/open-api/api.ts index bbfc64f7e..0ec6fc438 100644 --- a/web/src/api/open-api/api.ts +++ b/web/src/api/open-api/api.ts @@ -3160,12 +3160,13 @@ export const AlbumApiAxiosParamCreator = function (configuration?: Configuration /** * * @param {string} albumId + * @param {string} [name] * @param {number} [skip] * @param {string} [key] * @param {*} [options] Override http request option. * @throws {RequiredError} */ - downloadArchive: async (albumId: string, skip?: number, key?: string, options: AxiosRequestConfig = {}): Promise => { + downloadArchive: async (albumId: string, name?: string, skip?: number, key?: string, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'albumId' is not null or undefined assertParamExists('downloadArchive', 'albumId', albumId) const localVarPath = `/album/{albumId}/download` @@ -3187,6 +3188,10 @@ export const AlbumApiAxiosParamCreator = function (configuration?: Configuration // authentication cookie required + if (name !== undefined) { + localVarQueryParameter['name'] = name; + } + if (skip !== undefined) { localVarQueryParameter['skip'] = skip; } @@ -3529,13 +3534,14 @@ export const AlbumApiFp = function(configuration?: Configuration) { /** * * @param {string} albumId + * @param {string} [name] * @param {number} [skip] * @param {string} [key] * @param {*} [options] Override http request option. * @throws {RequiredError} */ - async downloadArchive(albumId: string, skip?: number, key?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.downloadArchive(albumId, skip, key, options); + async downloadArchive(albumId: string, name?: string, skip?: number, key?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.downloadArchive(albumId, name, skip, key, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** @@ -3663,13 +3669,14 @@ export const AlbumApiFactory = function (configuration?: Configuration, basePath /** * * @param {string} albumId + * @param {string} [name] * @param {number} [skip] * @param {string} [key] * @param {*} [options] Override http request option. * @throws {RequiredError} */ - downloadArchive(albumId: string, skip?: number, key?: string, options?: any): AxiosPromise { - return localVarFp.downloadArchive(albumId, skip, key, options).then((request) => request(axios, basePath)); + downloadArchive(albumId: string, name?: string, skip?: number, key?: string, options?: any): AxiosPromise { + return localVarFp.downloadArchive(albumId, name, skip, key, options).then((request) => request(axios, basePath)); }, /** * @@ -3800,14 +3807,15 @@ export class AlbumApi extends BaseAPI { /** * * @param {string} albumId + * @param {string} [name] * @param {number} [skip] * @param {string} [key] * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof AlbumApi */ - public downloadArchive(albumId: string, skip?: number, key?: string, options?: AxiosRequestConfig) { - return AlbumApiFp(this.configuration).downloadArchive(albumId, skip, key, options).then((request) => request(this.axios, this.basePath)); + public downloadArchive(albumId: string, name?: string, skip?: number, key?: string, options?: AxiosRequestConfig) { + return AlbumApiFp(this.configuration).downloadArchive(albumId, name, skip, key, options).then((request) => request(this.axios, this.basePath)); } /** @@ -4195,12 +4203,13 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration }, /** * Current this is not used in any UI element + * @param {string} [name] * @param {number} [skip] * @param {string} [key] * @param {*} [options] Override http request option. * @throws {RequiredError} */ - downloadLibrary: async (skip?: number, key?: string, options: AxiosRequestConfig = {}): Promise => { + downloadLibrary: async (name?: string, skip?: number, key?: string, options: AxiosRequestConfig = {}): Promise => { const localVarPath = `/asset/download-library`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); @@ -4219,6 +4228,10 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration // authentication cookie required + if (name !== undefined) { + localVarQueryParameter['name'] = name; + } + if (skip !== undefined) { localVarQueryParameter['skip'] = skip; } @@ -5029,13 +5042,14 @@ export const AssetApiFp = function(configuration?: Configuration) { }, /** * Current this is not used in any UI element + * @param {string} [name] * @param {number} [skip] * @param {string} [key] * @param {*} [options] Override http request option. * @throws {RequiredError} */ - async downloadLibrary(skip?: number, key?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.downloadLibrary(skip, key, options); + async downloadLibrary(name?: string, skip?: number, key?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.downloadLibrary(name, skip, key, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** @@ -5284,13 +5298,14 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath }, /** * Current this is not used in any UI element + * @param {string} [name] * @param {number} [skip] * @param {string} [key] * @param {*} [options] Override http request option. * @throws {RequiredError} */ - downloadLibrary(skip?: number, key?: string, options?: any): AxiosPromise { - return localVarFp.downloadLibrary(skip, key, options).then((request) => request(axios, basePath)); + downloadLibrary(name?: string, skip?: number, key?: string, options?: any): AxiosPromise { + return localVarFp.downloadLibrary(name, skip, key, options).then((request) => request(axios, basePath)); }, /** * Get all AssetEntity belong to the user @@ -5537,14 +5552,15 @@ export class AssetApi extends BaseAPI { /** * Current this is not used in any UI element + * @param {string} [name] * @param {number} [skip] * @param {string} [key] * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof AssetApi */ - public downloadLibrary(skip?: number, key?: string, options?: AxiosRequestConfig) { - return AssetApiFp(this.configuration).downloadLibrary(skip, key, options).then((request) => request(this.axios, this.basePath)); + public downloadLibrary(name?: string, skip?: number, key?: string, options?: AxiosRequestConfig) { + return AssetApiFp(this.configuration).downloadLibrary(name, skip, key, options).then((request) => request(this.axios, this.basePath)); } /** diff --git a/web/src/lib/components/album-page/album-viewer.svelte b/web/src/lib/components/album-page/album-viewer.svelte index 4ce42a432..e7c4f42a5 100644 --- a/web/src/lib/components/album-page/album-viewer.svelte +++ b/web/src/lib/components/album-page/album-viewer.svelte @@ -264,6 +264,7 @@ const { data, status, headers } = await api.albumApi.downloadArchive( album.id, + undefined, skip || undefined, sharedLink?.key, {