diff --git a/e2e/src/api/specs/timeline.e2e-spec.ts b/e2e/src/api/specs/timeline.e2e-spec.ts index 16bddac645..5a1a77ad64 100644 --- a/e2e/src/api/specs/timeline.e2e-spec.ts +++ b/e2e/src/api/specs/timeline.e2e-spec.ts @@ -1,4 +1,4 @@ -import { AssetMediaResponseDto, LoginResponseDto, SharedLinkType, TimeBucketSize } from '@immich/sdk'; +import { AssetMediaResponseDto, LoginResponseDto, SharedLinkType } from '@immich/sdk'; import { DateTime } from 'luxon'; import { createUserDto } from 'src/fixtures'; import { errorDto } from 'src/responses'; diff --git a/mobile/openapi/README.md b/mobile/openapi/README.md index 8ab2fa1c4e..0f4786dd55 100644 --- a/mobile/openapi/README.md +++ b/mobile/openapi/README.md @@ -478,11 +478,10 @@ Class | Method | HTTP request | Description - [TemplateResponseDto](doc//TemplateResponseDto.md) - [TestEmailResponseDto](doc//TestEmailResponseDto.md) - [TimeBucketAssetResponseDto](doc//TimeBucketAssetResponseDto.md) - - [TimeBucketAssetResponseDtoDurationInner](doc//TimeBucketAssetResponseDtoDurationInner.md) - - [TimeBucketAssetResponseDtoStackInner](doc//TimeBucketAssetResponseDtoStackInner.md) - [TimeBucketResponseDto](doc//TimeBucketResponseDto.md) - [TimeBucketsResponseDto](doc//TimeBucketsResponseDto.md) - [TimelineAssetDescriptionDto](doc//TimelineAssetDescriptionDto.md) + - [TimelineStackResponseDto](doc//TimelineStackResponseDto.md) - [ToneMapping](doc//ToneMapping.md) - [TranscodeHWAccel](doc//TranscodeHWAccel.md) - [TranscodePolicy](doc//TranscodePolicy.md) diff --git a/mobile/openapi/lib/api.dart b/mobile/openapi/lib/api.dart index c0ac7557d9..2435be0b47 100644 --- a/mobile/openapi/lib/api.dart +++ b/mobile/openapi/lib/api.dart @@ -282,11 +282,10 @@ part 'model/template_dto.dart'; part 'model/template_response_dto.dart'; part 'model/test_email_response_dto.dart'; part 'model/time_bucket_asset_response_dto.dart'; -part 'model/time_bucket_asset_response_dto_duration_inner.dart'; -part 'model/time_bucket_asset_response_dto_stack_inner.dart'; part 'model/time_bucket_response_dto.dart'; part 'model/time_buckets_response_dto.dart'; part 'model/timeline_asset_description_dto.dart'; +part 'model/timeline_stack_response_dto.dart'; part 'model/tone_mapping.dart'; part 'model/transcode_hw_accel.dart'; part 'model/transcode_policy.dart'; diff --git a/mobile/openapi/lib/api_client.dart b/mobile/openapi/lib/api_client.dart index 4a0e0cc219..3bb7460bf9 100644 --- a/mobile/openapi/lib/api_client.dart +++ b/mobile/openapi/lib/api_client.dart @@ -620,16 +620,14 @@ class ApiClient { return TestEmailResponseDto.fromJson(value); case 'TimeBucketAssetResponseDto': return TimeBucketAssetResponseDto.fromJson(value); - case 'TimeBucketAssetResponseDtoDurationInner': - return TimeBucketAssetResponseDtoDurationInner.fromJson(value); - case 'TimeBucketAssetResponseDtoStackInner': - return TimeBucketAssetResponseDtoStackInner.fromJson(value); case 'TimeBucketResponseDto': return TimeBucketResponseDto.fromJson(value); case 'TimeBucketsResponseDto': return TimeBucketsResponseDto.fromJson(value); case 'TimelineAssetDescriptionDto': return TimelineAssetDescriptionDto.fromJson(value); + case 'TimelineStackResponseDto': + return TimelineStackResponseDto.fromJson(value); case 'ToneMapping': return ToneMappingTypeTransformer().decode(value); case 'TranscodeHWAccel': diff --git a/mobile/openapi/lib/model/time_bucket_asset_response_dto.dart b/mobile/openapi/lib/model/time_bucket_asset_response_dto.dart index 1255f636b7..22c82f69ad 100644 --- a/mobile/openapi/lib/model/time_bucket_asset_response_dto.dart +++ b/mobile/openapi/lib/model/time_bucket_asset_response_dto.dart @@ -32,7 +32,7 @@ class TimeBucketAssetResponseDto { List description; - List duration; + List duration; List id; @@ -46,19 +46,19 @@ class TimeBucketAssetResponseDto { List isVideo; - List livePhotoVideoId; + List livePhotoVideoId; List localDateTime; List ownerId; - List projectionType; + List projectionType; List ratio; - List stack; + List stack; - List thumbhash; + List thumbhash; @override bool operator ==(Object other) => identical(this, other) || other is TimeBucketAssetResponseDto && @@ -130,7 +130,9 @@ class TimeBucketAssetResponseDto { return TimeBucketAssetResponseDto( description: TimelineAssetDescriptionDto.listFromJson(json[r'description']), - duration: TimeBucketAssetResponseDtoDurationInner.listFromJson(json[r'duration']), + duration: json[r'duration'] is Iterable + ? (json[r'duration'] as Iterable).cast().toList(growable: false) + : const [], id: json[r'id'] is Iterable ? (json[r'id'] as Iterable).cast().toList(growable: false) : const [], @@ -149,17 +151,23 @@ class TimeBucketAssetResponseDto { isVideo: json[r'isVideo'] is Iterable ? (json[r'isVideo'] as Iterable).cast().toList(growable: false) : const [], - livePhotoVideoId: TimeBucketAssetResponseDtoDurationInner.listFromJson(json[r'livePhotoVideoId']), + livePhotoVideoId: json[r'livePhotoVideoId'] is Iterable + ? (json[r'livePhotoVideoId'] as Iterable).cast().toList(growable: false) + : const [], localDateTime: DateTime.listFromJson(json[r'localDateTime']), ownerId: json[r'ownerId'] is Iterable ? (json[r'ownerId'] as Iterable).cast().toList(growable: false) : const [], - projectionType: TimeBucketAssetResponseDtoDurationInner.listFromJson(json[r'projectionType']), + projectionType: json[r'projectionType'] is Iterable + ? (json[r'projectionType'] as Iterable).cast().toList(growable: false) + : const [], ratio: json[r'ratio'] is Iterable ? (json[r'ratio'] as Iterable).cast().toList(growable: false) : const [], - stack: TimeBucketAssetResponseDtoStackInner.listFromJson(json[r'stack']), - thumbhash: TimeBucketAssetResponseDtoDurationInner.listFromJson(json[r'thumbhash']), + stack: TimelineStackResponseDto.listFromJson(json[r'stack']), + thumbhash: json[r'thumbhash'] is Iterable + ? (json[r'thumbhash'] as Iterable).cast().toList(growable: false) + : const [], ); } return null; diff --git a/mobile/openapi/lib/model/time_bucket_asset_response_dto_duration_inner.dart b/mobile/openapi/lib/model/time_bucket_asset_response_dto_duration_inner.dart deleted file mode 100644 index b9b7527c3b..0000000000 --- a/mobile/openapi/lib/model/time_bucket_asset_response_dto_duration_inner.dart +++ /dev/null @@ -1,91 +0,0 @@ -// -// AUTO-GENERATED FILE, DO NOT MODIFY! -// -// @dart=2.18 - -// ignore_for_file: unused_element, unused_import -// ignore_for_file: always_put_required_named_parameters_first -// ignore_for_file: constant_identifier_names -// ignore_for_file: lines_longer_than_80_chars - -part of openapi.api; - -class TimeBucketAssetResponseDtoDurationInner { - /// Returns a new [TimeBucketAssetResponseDtoDurationInner] instance. - TimeBucketAssetResponseDtoDurationInner({ - }); - - @override - bool operator ==(Object other) => identical(this, other) || other is TimeBucketAssetResponseDtoDurationInner && - - @override - int get hashCode => - // ignore: unnecessary_parenthesis - - @override - String toString() => 'TimeBucketAssetResponseDtoDurationInner[]'; - - Map toJson() { - final json = {}; - return json; - } - - /// Returns a new [TimeBucketAssetResponseDtoDurationInner] instance and imports its values from - /// [value] if it's a [Map], null otherwise. - // ignore: prefer_constructors_over_static_methods - static TimeBucketAssetResponseDtoDurationInner? fromJson(dynamic value) { - upgradeDto(value, "TimeBucketAssetResponseDtoDurationInner"); - if (value is Map) { - final json = value.cast(); - - return TimeBucketAssetResponseDtoDurationInner( - ); - } - return null; - } - - static List listFromJson(dynamic json, {bool growable = false,}) { - final result = []; - if (json is List && json.isNotEmpty) { - for (final row in json) { - final value = TimeBucketAssetResponseDtoDurationInner.fromJson(row); - if (value != null) { - result.add(value); - } - } - } - return result.toList(growable: growable); - } - - static Map mapFromJson(dynamic json) { - final map = {}; - if (json is Map && json.isNotEmpty) { - json = json.cast(); // ignore: parameter_assignments - for (final entry in json.entries) { - final value = TimeBucketAssetResponseDtoDurationInner.fromJson(entry.value); - if (value != null) { - map[entry.key] = value; - } - } - } - return map; - } - - // maps a json object with a list of TimeBucketAssetResponseDtoDurationInner-objects as value to a dart map - static Map> mapListFromJson(dynamic json, {bool growable = false,}) { - final map = >{}; - if (json is Map && json.isNotEmpty) { - // ignore: parameter_assignments - json = json.cast(); - for (final entry in json.entries) { - map[entry.key] = TimeBucketAssetResponseDtoDurationInner.listFromJson(entry.value, growable: growable,); - } - } - return map; - } - - /// The list of required keys that must be present in a JSON. - static const requiredKeys = { - }; -} - diff --git a/mobile/openapi/lib/model/time_bucket_asset_response_dto_stack_inner.dart b/mobile/openapi/lib/model/time_bucket_asset_response_dto_stack_inner.dart deleted file mode 100644 index e351edc3b6..0000000000 --- a/mobile/openapi/lib/model/time_bucket_asset_response_dto_stack_inner.dart +++ /dev/null @@ -1,91 +0,0 @@ -// -// AUTO-GENERATED FILE, DO NOT MODIFY! -// -// @dart=2.18 - -// ignore_for_file: unused_element, unused_import -// ignore_for_file: always_put_required_named_parameters_first -// ignore_for_file: constant_identifier_names -// ignore_for_file: lines_longer_than_80_chars - -part of openapi.api; - -class TimeBucketAssetResponseDtoStackInner { - /// Returns a new [TimeBucketAssetResponseDtoStackInner] instance. - TimeBucketAssetResponseDtoStackInner({ - }); - - @override - bool operator ==(Object other) => identical(this, other) || other is TimeBucketAssetResponseDtoStackInner && - - @override - int get hashCode => - // ignore: unnecessary_parenthesis - - @override - String toString() => 'TimeBucketAssetResponseDtoStackInner[]'; - - Map toJson() { - final json = {}; - return json; - } - - /// Returns a new [TimeBucketAssetResponseDtoStackInner] instance and imports its values from - /// [value] if it's a [Map], null otherwise. - // ignore: prefer_constructors_over_static_methods - static TimeBucketAssetResponseDtoStackInner? fromJson(dynamic value) { - upgradeDto(value, "TimeBucketAssetResponseDtoStackInner"); - if (value is Map) { - final json = value.cast(); - - return TimeBucketAssetResponseDtoStackInner( - ); - } - return null; - } - - static List listFromJson(dynamic json, {bool growable = false,}) { - final result = []; - if (json is List && json.isNotEmpty) { - for (final row in json) { - final value = TimeBucketAssetResponseDtoStackInner.fromJson(row); - if (value != null) { - result.add(value); - } - } - } - return result.toList(growable: growable); - } - - static Map mapFromJson(dynamic json) { - final map = {}; - if (json is Map && json.isNotEmpty) { - json = json.cast(); // ignore: parameter_assignments - for (final entry in json.entries) { - final value = TimeBucketAssetResponseDtoStackInner.fromJson(entry.value); - if (value != null) { - map[entry.key] = value; - } - } - } - return map; - } - - // maps a json object with a list of TimeBucketAssetResponseDtoStackInner-objects as value to a dart map - static Map> mapListFromJson(dynamic json, {bool growable = false,}) { - final map = >{}; - if (json is Map && json.isNotEmpty) { - // ignore: parameter_assignments - json = json.cast(); - for (final entry in json.entries) { - map[entry.key] = TimeBucketAssetResponseDtoStackInner.listFromJson(entry.value, growable: growable,); - } - } - return map; - } - - /// The list of required keys that must be present in a JSON. - static const requiredKeys = { - }; -} - diff --git a/mobile/openapi/lib/model/timeline_stack_response_dto.dart b/mobile/openapi/lib/model/timeline_stack_response_dto.dart new file mode 100644 index 0000000000..132790ef03 --- /dev/null +++ b/mobile/openapi/lib/model/timeline_stack_response_dto.dart @@ -0,0 +1,115 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.18 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +part of openapi.api; + +class TimelineStackResponseDto { + /// Returns a new [TimelineStackResponseDto] instance. + TimelineStackResponseDto({ + required this.assetCount, + required this.id, + required this.primaryAssetId, + }); + + num assetCount; + + String id; + + String primaryAssetId; + + @override + bool operator ==(Object other) => identical(this, other) || other is TimelineStackResponseDto && + other.assetCount == assetCount && + other.id == id && + other.primaryAssetId == primaryAssetId; + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (assetCount.hashCode) + + (id.hashCode) + + (primaryAssetId.hashCode); + + @override + String toString() => 'TimelineStackResponseDto[assetCount=$assetCount, id=$id, primaryAssetId=$primaryAssetId]'; + + Map toJson() { + final json = {}; + json[r'assetCount'] = this.assetCount; + json[r'id'] = this.id; + json[r'primaryAssetId'] = this.primaryAssetId; + return json; + } + + /// Returns a new [TimelineStackResponseDto] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static TimelineStackResponseDto? fromJson(dynamic value) { + upgradeDto(value, "TimelineStackResponseDto"); + if (value is Map) { + final json = value.cast(); + + return TimelineStackResponseDto( + assetCount: num.parse('${json[r'assetCount']}'), + id: mapValueOfType(json, r'id')!, + primaryAssetId: mapValueOfType(json, r'primaryAssetId')!, + ); + } + return null; + } + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = TimelineStackResponseDto.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } + + static Map mapFromJson(dynamic json) { + final map = {}; + if (json is Map && json.isNotEmpty) { + json = json.cast(); // ignore: parameter_assignments + for (final entry in json.entries) { + final value = TimelineStackResponseDto.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of TimelineStackResponseDto-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; + if (json is Map && json.isNotEmpty) { + // ignore: parameter_assignments + json = json.cast(); + for (final entry in json.entries) { + map[entry.key] = TimelineStackResponseDto.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + 'assetCount', + 'id', + 'primaryAssetId', + }; +} + diff --git a/open-api/bin/generate-open-api.sh b/open-api/bin/generate-open-api.sh index e2badc6dff..8fa5dd0da4 100755 --- a/open-api/bin/generate-open-api.sh +++ b/open-api/bin/generate-open-api.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -OPENAPI_GENERATOR_VERSION=v7.8.0 +OPENAPI_GENERATOR_VERSION=v7.12.0 # usage: ./bin/generate-open-api.sh diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json index db435795a2..b96405c413 100644 --- a/open-api/immich-openapi-specs.json +++ b/open-api/immich-openapi-specs.json @@ -13571,14 +13571,7 @@ "duration": { "default": [], "items": { - "oneOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ] + "type": "string" }, "type": "array" }, @@ -13627,14 +13620,7 @@ "livePhotoVideoId": { "default": [], "items": { - "oneOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ] + "type": "string" }, "type": "array" }, @@ -13656,14 +13642,7 @@ "projectionType": { "default": [], "items": { - "oneOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ] + "type": "string" }, "type": "array" }, @@ -13677,28 +13656,14 @@ "stack": { "default": [], "items": { - "oneOf": [ - { - "type": "TimelineStackResponseDto" - }, - { - "type": "null" - } - ] + "$ref": "#/components/schemas/TimelineStackResponseDto" }, "type": "array" }, "thumbhash": { "default": [], "items": { - "oneOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ] + "type": "string" }, "type": "array" } @@ -13769,6 +13734,25 @@ ], "type": "object" }, + "TimelineStackResponseDto": { + "properties": { + "assetCount": { + "type": "number" + }, + "id": { + "type": "string" + }, + "primaryAssetId": { + "type": "string" + } + }, + "required": [ + "assetCount", + "id", + "primaryAssetId" + ], + "type": "object" + }, "ToneMapping": { "enum": [ "hable", diff --git a/open-api/typescript-sdk/src/fetch-client.ts b/open-api/typescript-sdk/src/fetch-client.ts index 3e2fc81a2b..9f163939f6 100644 --- a/open-api/typescript-sdk/src/fetch-client.ts +++ b/open-api/typescript-sdk/src/fetch-client.ts @@ -1388,22 +1388,27 @@ export type TimelineAssetDescriptionDto = { city: string | null; country: string | null; }; +export type TimelineStackResponseDto = { + assetCount: number; + id: string; + primaryAssetId: string; +}; export type TimeBucketAssetResponseDto = { description: TimelineAssetDescriptionDto[]; - duration: (string | null)[]; + duration: string[]; id: string[]; isArchived: number[]; isFavorite: number[]; isImage: number[]; isTrashed: number[]; isVideo: number[]; - livePhotoVideoId: (string | null)[]; + livePhotoVideoId: string[]; localDateTime: string[]; ownerId: string[]; - projectionType: (string | null)[]; + projectionType: string[]; ratio: number[]; - stack: (any | null)[]; - thumbhash: (string | null)[]; + stack: TimelineStackResponseDto[]; + thumbhash: string[]; }; export type TimeBucketResponseDto = { bucketAssets: TimeBucketAssetResponseDto; diff --git a/server/src/dtos/time-bucket.dto.ts b/server/src/dtos/time-bucket.dto.ts index b55e4daa7c..ced47c00ff 100644 --- a/server/src/dtos/time-bucket.dto.ts +++ b/server/src/dtos/time-bucket.dto.ts @@ -96,82 +96,22 @@ export class TimeBucketAssetResponseDto implements TimeBucketAssets { @ApiProperty() isVideo: number[] = []; - @ApiProperty({ - type: 'array', - items: { - oneOf: [ - { - type: 'string', - }, - { - type: 'null', - }, - ], - }, - }) + @ApiProperty({ type: [String] }) thumbhash: (string | null)[] = []; @ApiProperty() localDateTime: Date[] = []; - @ApiProperty({ - type: 'array', - items: { - oneOf: [ - { - type: 'string', - }, - { - type: 'null', - }, - ], - }, - }) + @ApiProperty({ type: [String] }) duration: (string | null)[] = []; - @ApiProperty({ - type: 'array', - items: { - oneOf: [ - { - type: 'TimelineStackResponseDto', - }, - { - type: 'null', - }, - ], - }, - }) + @ApiProperty({ type: [TimelineStackResponseDto] }) stack: (TimelineStackResponseDto | null)[] = []; - @ApiProperty({ - type: 'array', - items: { - oneOf: [ - { - type: 'string', - }, - { - type: 'null', - }, - ], - }, - }) + @ApiProperty({ type: [String] }) projectionType: (string | null)[] = []; - @ApiProperty({ - type: 'array', - items: { - oneOf: [ - { - type: 'string', - }, - { - type: 'null', - }, - ], - }, - }) + @ApiProperty({ type: [String] }) livePhotoVideoId: (string | null)[] = []; @ApiProperty() diff --git a/web/src/test-data/factories/asset-factory.ts b/web/src/test-data/factories/asset-factory.ts index c2906568b1..80158492c1 100644 --- a/web/src/test-data/factories/asset-factory.ts +++ b/web/src/test-data/factories/asset-factory.ts @@ -75,20 +75,20 @@ export const toResponseDto = (...timelineAsset: TimelineAsset[]) => { }; for (const asset of timelineAsset) { bucketAssets.description.push(asset.description); - bucketAssets.duration.push(asset.duration || 0); + bucketAssets.duration.push(asset.duration); bucketAssets.id.push(asset.id); bucketAssets.isArchived.push(asset.isArchived ? 1 : 0); bucketAssets.isFavorite.push(asset.isFavorite ? 1 : 0); bucketAssets.isImage.push(asset.isImage ? 1 : 0); bucketAssets.isTrashed.push(asset.isTrashed ? 1 : 0); bucketAssets.isVideo.push(asset.isVideo ? 1 : 0); - bucketAssets.livePhotoVideoId.push(asset.livePhotoVideoId || 0); + bucketAssets.livePhotoVideoId.push(asset.livePhotoVideoId); bucketAssets.localDateTime.push(asset.localDateTime); bucketAssets.ownerId.push(asset.ownerId); - bucketAssets.projectionType.push(asset.projectionType || 0); + bucketAssets.projectionType.push(asset.projectionType); bucketAssets.ratio.push(asset.ratio); bucketAssets.stack.push(asset.stack as TimelineStackResponseDto); - bucketAssets.thumbhash.push(asset.thumbhash || 0); + bucketAssets.thumbhash.push(asset.thumbhash); } const response: TimeBucketResponseDto = { bucketAssets,