mirror of
https://github.com/immich-app/immich.git
synced 2025-07-09 03:04:16 -04:00
Use nulls, make-sql
This commit is contained in:
parent
0ed2a2fd2e
commit
aea2c9506d
@ -104,7 +104,7 @@ describe('/timeline', () => {
|
|||||||
const req1 = await request(app)
|
const req1 = await request(app)
|
||||||
.get('/timeline/buckets')
|
.get('/timeline/buckets')
|
||||||
.set('Authorization', `Bearer ${timeBucketUser.accessToken}`)
|
.set('Authorization', `Bearer ${timeBucketUser.accessToken}`)
|
||||||
.query({ size: TimeBucketSize.Month, withPartners: true, isArchived: true });
|
.query({ withPartners: true, isArchived: true });
|
||||||
|
|
||||||
expect(req1.status).toBe(400);
|
expect(req1.status).toBe(400);
|
||||||
expect(req1.body).toEqual(errorDto.badRequest());
|
expect(req1.body).toEqual(errorDto.badRequest());
|
||||||
@ -112,7 +112,7 @@ describe('/timeline', () => {
|
|||||||
const req2 = await request(app)
|
const req2 = await request(app)
|
||||||
.get('/timeline/buckets')
|
.get('/timeline/buckets')
|
||||||
.set('Authorization', `Bearer ${user.accessToken}`)
|
.set('Authorization', `Bearer ${user.accessToken}`)
|
||||||
.query({ size: TimeBucketSize.Month, withPartners: true, isArchived: undefined });
|
.query({ withPartners: true, isArchived: undefined });
|
||||||
|
|
||||||
expect(req2.status).toBe(400);
|
expect(req2.status).toBe(400);
|
||||||
expect(req2.body).toEqual(errorDto.badRequest());
|
expect(req2.body).toEqual(errorDto.badRequest());
|
||||||
@ -122,7 +122,7 @@ describe('/timeline', () => {
|
|||||||
const req1 = await request(app)
|
const req1 = await request(app)
|
||||||
.get('/timeline/buckets')
|
.get('/timeline/buckets')
|
||||||
.set('Authorization', `Bearer ${timeBucketUser.accessToken}`)
|
.set('Authorization', `Bearer ${timeBucketUser.accessToken}`)
|
||||||
.query({ size: TimeBucketSize.Month, withPartners: true, isFavorite: true });
|
.query({ withPartners: true, isFavorite: true });
|
||||||
|
|
||||||
expect(req1.status).toBe(400);
|
expect(req1.status).toBe(400);
|
||||||
expect(req1.body).toEqual(errorDto.badRequest());
|
expect(req1.body).toEqual(errorDto.badRequest());
|
||||||
@ -130,7 +130,7 @@ describe('/timeline', () => {
|
|||||||
const req2 = await request(app)
|
const req2 = await request(app)
|
||||||
.get('/timeline/buckets')
|
.get('/timeline/buckets')
|
||||||
.set('Authorization', `Bearer ${timeBucketUser.accessToken}`)
|
.set('Authorization', `Bearer ${timeBucketUser.accessToken}`)
|
||||||
.query({ size: TimeBucketSize.Month, withPartners: true, isFavorite: false });
|
.query({ withPartners: true, isFavorite: false });
|
||||||
|
|
||||||
expect(req2.status).toBe(400);
|
expect(req2.status).toBe(400);
|
||||||
expect(req2.body).toEqual(errorDto.badRequest());
|
expect(req2.body).toEqual(errorDto.badRequest());
|
||||||
@ -140,7 +140,7 @@ describe('/timeline', () => {
|
|||||||
const req = await request(app)
|
const req = await request(app)
|
||||||
.get('/timeline/buckets')
|
.get('/timeline/buckets')
|
||||||
.set('Authorization', `Bearer ${user.accessToken}`)
|
.set('Authorization', `Bearer ${user.accessToken}`)
|
||||||
.query({ size: TimeBucketSize.Month, withPartners: true, isTrashed: true });
|
.query({ withPartners: true, isTrashed: true });
|
||||||
|
|
||||||
expect(req.status).toBe(400);
|
expect(req.status).toBe(400);
|
||||||
expect(req.body).toEqual(errorDto.badRequest());
|
expect(req.body).toEqual(errorDto.badRequest());
|
||||||
@ -150,7 +150,6 @@ describe('/timeline', () => {
|
|||||||
describe('GET /timeline/bucket', () => {
|
describe('GET /timeline/bucket', () => {
|
||||||
it('should require authentication', async () => {
|
it('should require authentication', async () => {
|
||||||
const { status, body } = await request(app).get('/timeline/bucket').query({
|
const { status, body } = await request(app).get('/timeline/bucket').query({
|
||||||
size: TimeBucketSize.Month,
|
|
||||||
timeBucket: '1900-01-01',
|
timeBucket: '1900-01-01',
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -161,7 +160,7 @@ describe('/timeline', () => {
|
|||||||
it('should handle 5 digit years', async () => {
|
it('should handle 5 digit years', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.get('/timeline/bucket')
|
.get('/timeline/bucket')
|
||||||
.query({ size: TimeBucketSize.Month, timeBucket: '012345-01-01' })
|
.query({ timeBucket: '012345-01-01' })
|
||||||
.set('Authorization', `Bearer ${timeBucketUser.accessToken}`);
|
.set('Authorization', `Bearer ${timeBucketUser.accessToken}`);
|
||||||
|
|
||||||
expect(status).toBe(200);
|
expect(status).toBe(200);
|
||||||
@ -173,7 +172,7 @@ describe('/timeline', () => {
|
|||||||
// const { status, body } = await request(app)
|
// const { status, body } = await request(app)
|
||||||
// .get('/timeline/bucket')
|
// .get('/timeline/bucket')
|
||||||
// .set('Authorization', `Bearer ${user.accessToken}`)
|
// .set('Authorization', `Bearer ${user.accessToken}`)
|
||||||
// .query({ size: TimeBucketSize.Month, timeBucket: 'foo' });
|
// .query({ timeBucket: 'foo' });
|
||||||
|
|
||||||
// expect(status).toBe(400);
|
// expect(status).toBe(400);
|
||||||
// expect(body).toEqual(errorDto.badRequest);
|
// expect(body).toEqual(errorDto.badRequest);
|
||||||
@ -183,7 +182,7 @@ describe('/timeline', () => {
|
|||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.get('/timeline/bucket')
|
.get('/timeline/bucket')
|
||||||
.set('Authorization', `Bearer ${timeBucketUser.accessToken}`)
|
.set('Authorization', `Bearer ${timeBucketUser.accessToken}`)
|
||||||
.query({ size: TimeBucketSize.Month, timeBucket: '1970-02-10' });
|
.query({ timeBucket: '1970-02-10' });
|
||||||
|
|
||||||
expect(status).toBe(200);
|
expect(status).toBe(200);
|
||||||
expect(body).toEqual([]);
|
expect(body).toEqual([]);
|
||||||
|
2
mobile/openapi/README.md
generated
2
mobile/openapi/README.md
generated
@ -479,10 +479,10 @@ Class | Method | HTTP request | Description
|
|||||||
- [TestEmailResponseDto](doc//TestEmailResponseDto.md)
|
- [TestEmailResponseDto](doc//TestEmailResponseDto.md)
|
||||||
- [TimeBucketAssetResponseDto](doc//TimeBucketAssetResponseDto.md)
|
- [TimeBucketAssetResponseDto](doc//TimeBucketAssetResponseDto.md)
|
||||||
- [TimeBucketAssetResponseDtoDurationInner](doc//TimeBucketAssetResponseDtoDurationInner.md)
|
- [TimeBucketAssetResponseDtoDurationInner](doc//TimeBucketAssetResponseDtoDurationInner.md)
|
||||||
|
- [TimeBucketAssetResponseDtoStackInner](doc//TimeBucketAssetResponseDtoStackInner.md)
|
||||||
- [TimeBucketResponseDto](doc//TimeBucketResponseDto.md)
|
- [TimeBucketResponseDto](doc//TimeBucketResponseDto.md)
|
||||||
- [TimeBucketsResponseDto](doc//TimeBucketsResponseDto.md)
|
- [TimeBucketsResponseDto](doc//TimeBucketsResponseDto.md)
|
||||||
- [TimelineAssetDescriptionDto](doc//TimelineAssetDescriptionDto.md)
|
- [TimelineAssetDescriptionDto](doc//TimelineAssetDescriptionDto.md)
|
||||||
- [TimelineStackResponseDto](doc//TimelineStackResponseDto.md)
|
|
||||||
- [ToneMapping](doc//ToneMapping.md)
|
- [ToneMapping](doc//ToneMapping.md)
|
||||||
- [TranscodeHWAccel](doc//TranscodeHWAccel.md)
|
- [TranscodeHWAccel](doc//TranscodeHWAccel.md)
|
||||||
- [TranscodePolicy](doc//TranscodePolicy.md)
|
- [TranscodePolicy](doc//TranscodePolicy.md)
|
||||||
|
2
mobile/openapi/lib/api.dart
generated
2
mobile/openapi/lib/api.dart
generated
@ -283,10 +283,10 @@ part 'model/template_response_dto.dart';
|
|||||||
part 'model/test_email_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.dart';
|
||||||
part 'model/time_bucket_asset_response_dto_duration_inner.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_bucket_response_dto.dart';
|
||||||
part 'model/time_buckets_response_dto.dart';
|
part 'model/time_buckets_response_dto.dart';
|
||||||
part 'model/timeline_asset_description_dto.dart';
|
part 'model/timeline_asset_description_dto.dart';
|
||||||
part 'model/timeline_stack_response_dto.dart';
|
|
||||||
part 'model/tone_mapping.dart';
|
part 'model/tone_mapping.dart';
|
||||||
part 'model/transcode_hw_accel.dart';
|
part 'model/transcode_hw_accel.dart';
|
||||||
part 'model/transcode_policy.dart';
|
part 'model/transcode_policy.dart';
|
||||||
|
4
mobile/openapi/lib/api_client.dart
generated
4
mobile/openapi/lib/api_client.dart
generated
@ -622,14 +622,14 @@ class ApiClient {
|
|||||||
return TimeBucketAssetResponseDto.fromJson(value);
|
return TimeBucketAssetResponseDto.fromJson(value);
|
||||||
case 'TimeBucketAssetResponseDtoDurationInner':
|
case 'TimeBucketAssetResponseDtoDurationInner':
|
||||||
return TimeBucketAssetResponseDtoDurationInner.fromJson(value);
|
return TimeBucketAssetResponseDtoDurationInner.fromJson(value);
|
||||||
|
case 'TimeBucketAssetResponseDtoStackInner':
|
||||||
|
return TimeBucketAssetResponseDtoStackInner.fromJson(value);
|
||||||
case 'TimeBucketResponseDto':
|
case 'TimeBucketResponseDto':
|
||||||
return TimeBucketResponseDto.fromJson(value);
|
return TimeBucketResponseDto.fromJson(value);
|
||||||
case 'TimeBucketsResponseDto':
|
case 'TimeBucketsResponseDto':
|
||||||
return TimeBucketsResponseDto.fromJson(value);
|
return TimeBucketsResponseDto.fromJson(value);
|
||||||
case 'TimelineAssetDescriptionDto':
|
case 'TimelineAssetDescriptionDto':
|
||||||
return TimelineAssetDescriptionDto.fromJson(value);
|
return TimelineAssetDescriptionDto.fromJson(value);
|
||||||
case 'TimelineStackResponseDto':
|
|
||||||
return TimelineStackResponseDto.fromJson(value);
|
|
||||||
case 'ToneMapping':
|
case 'ToneMapping':
|
||||||
return ToneMappingTypeTransformer().decode(value);
|
return ToneMappingTypeTransformer().decode(value);
|
||||||
case 'TranscodeHWAccel':
|
case 'TranscodeHWAccel':
|
||||||
|
@ -56,7 +56,7 @@ class TimeBucketAssetResponseDto {
|
|||||||
|
|
||||||
List<num> ratio;
|
List<num> ratio;
|
||||||
|
|
||||||
List<TimelineStackResponseDto> stack;
|
List<TimeBucketAssetResponseDtoStackInner> stack;
|
||||||
|
|
||||||
List<TimeBucketAssetResponseDtoDurationInner> thumbhash;
|
List<TimeBucketAssetResponseDtoDurationInner> thumbhash;
|
||||||
|
|
||||||
@ -158,7 +158,7 @@ class TimeBucketAssetResponseDto {
|
|||||||
ratio: json[r'ratio'] is Iterable
|
ratio: json[r'ratio'] is Iterable
|
||||||
? (json[r'ratio'] as Iterable).cast<num>().toList(growable: false)
|
? (json[r'ratio'] as Iterable).cast<num>().toList(growable: false)
|
||||||
: const [],
|
: const [],
|
||||||
stack: TimelineStackResponseDto.listFromJson(json[r'stack']),
|
stack: TimeBucketAssetResponseDtoStackInner.listFromJson(json[r'stack']),
|
||||||
thumbhash: TimeBucketAssetResponseDtoDurationInner.listFromJson(json[r'thumbhash']),
|
thumbhash: TimeBucketAssetResponseDtoDurationInner.listFromJson(json[r'thumbhash']),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
91
mobile/openapi/lib/model/time_bucket_asset_response_dto_stack_inner.dart
generated
Normal file
91
mobile/openapi/lib/model/time_bucket_asset_response_dto_stack_inner.dart
generated
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
//
|
||||||
|
// 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<String, dynamic> toJson() {
|
||||||
|
final json = <String, dynamic>{};
|
||||||
|
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<String, dynamic>();
|
||||||
|
|
||||||
|
return TimeBucketAssetResponseDtoStackInner(
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static List<TimeBucketAssetResponseDtoStackInner> listFromJson(dynamic json, {bool growable = false,}) {
|
||||||
|
final result = <TimeBucketAssetResponseDtoStackInner>[];
|
||||||
|
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<String, TimeBucketAssetResponseDtoStackInner> mapFromJson(dynamic json) {
|
||||||
|
final map = <String, TimeBucketAssetResponseDtoStackInner>{};
|
||||||
|
if (json is Map && json.isNotEmpty) {
|
||||||
|
json = json.cast<String, dynamic>(); // 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<String, List<TimeBucketAssetResponseDtoStackInner>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
||||||
|
final map = <String, List<TimeBucketAssetResponseDtoStackInner>>{};
|
||||||
|
if (json is Map && json.isNotEmpty) {
|
||||||
|
// ignore: parameter_assignments
|
||||||
|
json = json.cast<String, dynamic>();
|
||||||
|
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 = <String>{
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -1,115 +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 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<String, dynamic> toJson() {
|
|
||||||
final json = <String, dynamic>{};
|
|
||||||
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<String, dynamic>();
|
|
||||||
|
|
||||||
return TimelineStackResponseDto(
|
|
||||||
assetCount: num.parse('${json[r'assetCount']}'),
|
|
||||||
id: mapValueOfType<String>(json, r'id')!,
|
|
||||||
primaryAssetId: mapValueOfType<String>(json, r'primaryAssetId')!,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
static List<TimelineStackResponseDto> listFromJson(dynamic json, {bool growable = false,}) {
|
|
||||||
final result = <TimelineStackResponseDto>[];
|
|
||||||
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<String, TimelineStackResponseDto> mapFromJson(dynamic json) {
|
|
||||||
final map = <String, TimelineStackResponseDto>{};
|
|
||||||
if (json is Map && json.isNotEmpty) {
|
|
||||||
json = json.cast<String, dynamic>(); // 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<String, List<TimelineStackResponseDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
|
||||||
final map = <String, List<TimelineStackResponseDto>>{};
|
|
||||||
if (json is Map && json.isNotEmpty) {
|
|
||||||
// ignore: parameter_assignments
|
|
||||||
json = json.cast<String, dynamic>();
|
|
||||||
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 = <String>{
|
|
||||||
'assetCount',
|
|
||||||
'id',
|
|
||||||
'primaryAssetId',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -13576,7 +13576,7 @@
|
|||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "number"
|
"type": "null"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -13632,7 +13632,7 @@
|
|||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "number"
|
"type": "null"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -13661,7 +13661,7 @@
|
|||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "number"
|
"type": "null"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -13677,7 +13677,14 @@
|
|||||||
"stack": {
|
"stack": {
|
||||||
"default": [],
|
"default": [],
|
||||||
"items": {
|
"items": {
|
||||||
"$ref": "#/components/schemas/TimelineStackResponseDto"
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"type": "TimelineStackResponseDto"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"type": "array"
|
"type": "array"
|
||||||
},
|
},
|
||||||
@ -13689,7 +13696,7 @@
|
|||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "number"
|
"type": "null"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -13762,25 +13769,6 @@
|
|||||||
],
|
],
|
||||||
"type": "object"
|
"type": "object"
|
||||||
},
|
},
|
||||||
"TimelineStackResponseDto": {
|
|
||||||
"properties": {
|
|
||||||
"assetCount": {
|
|
||||||
"type": "number"
|
|
||||||
},
|
|
||||||
"id": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"primaryAssetId": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"assetCount",
|
|
||||||
"id",
|
|
||||||
"primaryAssetId"
|
|
||||||
],
|
|
||||||
"type": "object"
|
|
||||||
},
|
|
||||||
"ToneMapping": {
|
"ToneMapping": {
|
||||||
"enum": [
|
"enum": [
|
||||||
"hable",
|
"hable",
|
||||||
|
@ -1388,27 +1388,22 @@ export type TimelineAssetDescriptionDto = {
|
|||||||
city: string | null;
|
city: string | null;
|
||||||
country: string | null;
|
country: string | null;
|
||||||
};
|
};
|
||||||
export type TimelineStackResponseDto = {
|
|
||||||
assetCount: number;
|
|
||||||
id: string;
|
|
||||||
primaryAssetId: string;
|
|
||||||
};
|
|
||||||
export type TimeBucketAssetResponseDto = {
|
export type TimeBucketAssetResponseDto = {
|
||||||
description: TimelineAssetDescriptionDto[];
|
description: TimelineAssetDescriptionDto[];
|
||||||
duration: (string | number)[];
|
duration: (string | null)[];
|
||||||
id: string[];
|
id: string[];
|
||||||
isArchived: number[];
|
isArchived: number[];
|
||||||
isFavorite: number[];
|
isFavorite: number[];
|
||||||
isImage: number[];
|
isImage: number[];
|
||||||
isTrashed: number[];
|
isTrashed: number[];
|
||||||
isVideo: number[];
|
isVideo: number[];
|
||||||
livePhotoVideoId: (string | number)[];
|
livePhotoVideoId: (string | null)[];
|
||||||
localDateTime: string[];
|
localDateTime: string[];
|
||||||
ownerId: string[];
|
ownerId: string[];
|
||||||
projectionType: (string | number)[];
|
projectionType: (string | null)[];
|
||||||
ratio: number[];
|
ratio: number[];
|
||||||
stack: TimelineStackResponseDto[];
|
stack: (any | null)[];
|
||||||
thumbhash: (string | number)[];
|
thumbhash: (string | null)[];
|
||||||
};
|
};
|
||||||
export type TimeBucketResponseDto = {
|
export type TimeBucketResponseDto = {
|
||||||
bucketAssets: TimeBucketAssetResponseDto;
|
bucketAssets: TimeBucketAssetResponseDto;
|
||||||
|
@ -72,7 +72,9 @@ class SqlGenerator {
|
|||||||
await rm(this.options.targetDir, { force: true, recursive: true });
|
await rm(this.options.targetDir, { force: true, recursive: true });
|
||||||
await mkdir(this.options.targetDir);
|
await mkdir(this.options.targetDir);
|
||||||
|
|
||||||
process.env.DB_HOSTNAME = 'localhost';
|
if (!process.env.DB_HOSTNAME) {
|
||||||
|
process.env.DB_HOSTNAME = 'localhost';
|
||||||
|
}
|
||||||
const { database, cls, otel } = new ConfigRepository().getEnv();
|
const { database, cls, otel } = new ConfigRepository().getEnv();
|
||||||
|
|
||||||
const moduleFixture = await Test.createTestingModule({
|
const moduleFixture = await Test.createTestingModule({
|
||||||
|
@ -183,7 +183,7 @@ export function mapAsset(entity: MapAsset, options: AssetMapOptions = {}): Asset
|
|||||||
tags: entity.tags?.map((tag) => mapTag(tag)),
|
tags: entity.tags?.map((tag) => mapTag(tag)),
|
||||||
people: peopleWithFaces(entity.faces),
|
people: peopleWithFaces(entity.faces),
|
||||||
unassignedFaces: entity.faces?.filter((face) => !face.person).map((a) => mapFacesWithoutPerson(a)),
|
unassignedFaces: entity.faces?.filter((face) => !face.person).map((a) => mapFacesWithoutPerson(a)),
|
||||||
checksum: hexOrBufferToBase64(entity.checksum),
|
checksum: hexOrBufferToBase64(entity.checksum)!,
|
||||||
stack: withStack ? mapStack(entity) : undefined,
|
stack: withStack ? mapStack(entity) : undefined,
|
||||||
isOffline: entity.isOffline,
|
isOffline: entity.isOffline,
|
||||||
hasMetadata: true,
|
hasMetadata: true,
|
||||||
|
@ -104,12 +104,12 @@ export class TimeBucketAssetResponseDto implements TimeBucketAssets {
|
|||||||
type: 'string',
|
type: 'string',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'number',
|
type: 'null',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
thumbhash: (string | number)[] = [];
|
thumbhash: (string | null)[] = [];
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
localDateTime: Date[] = [];
|
localDateTime: Date[] = [];
|
||||||
@ -122,15 +122,27 @@ export class TimeBucketAssetResponseDto implements TimeBucketAssets {
|
|||||||
type: 'string',
|
type: 'string',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'number',
|
type: 'null',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
duration: (string | number)[] = [];
|
duration: (string | null)[] = [];
|
||||||
|
|
||||||
@ApiProperty({ type: [TimelineStackResponseDto] })
|
@ApiProperty({
|
||||||
stack: (TimelineStackResponseDto | number)[] = [];
|
type: 'array',
|
||||||
|
items: {
|
||||||
|
oneOf: [
|
||||||
|
{
|
||||||
|
type: 'TimelineStackResponseDto',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'null',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
stack: (TimelineStackResponseDto | null)[] = [];
|
||||||
|
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
type: 'array',
|
type: 'array',
|
||||||
@ -140,12 +152,12 @@ export class TimeBucketAssetResponseDto implements TimeBucketAssets {
|
|||||||
type: 'string',
|
type: 'string',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'number',
|
type: 'null',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
projectionType: (string | number)[] = [];
|
projectionType: (string | null)[] = [];
|
||||||
|
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
type: 'array',
|
type: 'array',
|
||||||
@ -155,12 +167,12 @@ export class TimeBucketAssetResponseDto implements TimeBucketAssets {
|
|||||||
type: 'string',
|
type: 'string',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'number',
|
type: 'null',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
livePhotoVideoId: (string | number)[] = [];
|
livePhotoVideoId: (string | null)[] = [];
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
description: TimelineAssetDescriptionDto[] = [];
|
description: TimelineAssetDescriptionDto[] = [];
|
||||||
|
@ -255,8 +255,23 @@ order by
|
|||||||
|
|
||||||
-- AssetRepository.getTimeBucket
|
-- AssetRepository.getTimeBucket
|
||||||
select
|
select
|
||||||
"assets".*,
|
"assets"."id" as "id",
|
||||||
to_json("exif") as "exifInfo",
|
"assets"."ownerId",
|
||||||
|
"assets"."status",
|
||||||
|
"deletedAt",
|
||||||
|
"type",
|
||||||
|
"duration",
|
||||||
|
"isFavorite",
|
||||||
|
"isArchived",
|
||||||
|
"thumbhash",
|
||||||
|
"localDateTime",
|
||||||
|
"livePhotoVideoId",
|
||||||
|
"exif"."exifImageHeight" as "height",
|
||||||
|
"exifImageWidth" as "width",
|
||||||
|
"exif"."orientation",
|
||||||
|
"exif"."projectionType",
|
||||||
|
"exif"."city" as "city",
|
||||||
|
"exif"."country" as "country",
|
||||||
to_json("stacked_assets") as "stack"
|
to_json("stacked_assets") as "stack"
|
||||||
from
|
from
|
||||||
"assets"
|
"assets"
|
||||||
|
@ -142,7 +142,7 @@ export class SyncService extends BaseService {
|
|||||||
updateId,
|
updateId,
|
||||||
data: {
|
data: {
|
||||||
...data,
|
...data,
|
||||||
checksum: hexOrBufferToBase64(checksum),
|
checksum: hexOrBufferToBase64(checksum)!,
|
||||||
thumbhash: thumbhash ? hexOrBufferToBase64(thumbhash) : null,
|
thumbhash: thumbhash ? hexOrBufferToBase64(thumbhash) : null,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
@ -172,7 +172,7 @@ export class SyncService extends BaseService {
|
|||||||
updateId,
|
updateId,
|
||||||
data: {
|
data: {
|
||||||
...data,
|
...data,
|
||||||
checksum: hexOrBufferToBase64(checksum),
|
checksum: hexOrBufferToBase64(checksum)!,
|
||||||
thumbhash: thumbhash ? hexOrBufferToBase64(thumbhash) : null,
|
thumbhash: thumbhash ? hexOrBufferToBase64(thumbhash) : null,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
@ -75,12 +75,12 @@ export class TimelineService extends BaseService {
|
|||||||
bucketAssets.isArchived.push(item.isArchived ? 1 : 0);
|
bucketAssets.isArchived.push(item.isArchived ? 1 : 0);
|
||||||
bucketAssets.isFavorite.push(item.isFavorite ? 1 : 0);
|
bucketAssets.isFavorite.push(item.isFavorite ? 1 : 0);
|
||||||
bucketAssets.isTrashed.push(item.deletedAt === null ? 0 : 1);
|
bucketAssets.isTrashed.push(item.deletedAt === null ? 0 : 1);
|
||||||
bucketAssets.thumbhash.push(item.thumbhash ? hexOrBufferToBase64(item.thumbhash) : 0);
|
bucketAssets.thumbhash.push(hexOrBufferToBase64(item.thumbhash));
|
||||||
bucketAssets.localDateTime.push(item.localDateTime);
|
bucketAssets.localDateTime.push(item.localDateTime);
|
||||||
bucketAssets.stack.push(this.mapStack(item.stack) || 0);
|
bucketAssets.stack.push(this.mapStack(item.stack));
|
||||||
bucketAssets.duration.push(item.duration || 0);
|
bucketAssets.duration.push(item.duration);
|
||||||
bucketAssets.projectionType.push(item.projectionType || 0);
|
bucketAssets.projectionType.push(item.projectionType);
|
||||||
bucketAssets.livePhotoVideoId.push(item.livePhotoVideoId || 0);
|
bucketAssets.livePhotoVideoId.push(item.livePhotoVideoId);
|
||||||
bucketAssets.isImage.push(item.type === AssetType.IMAGE ? 1 : 0);
|
bucketAssets.isImage.push(item.type === AssetType.IMAGE ? 1 : 0);
|
||||||
bucketAssets.isVideo.push(item.type === AssetType.VIDEO ? 1 : 0);
|
bucketAssets.isVideo.push(item.type === AssetType.VIDEO ? 1 : 0);
|
||||||
bucketAssets.description.push({
|
bucketAssets.description.push({
|
||||||
@ -97,7 +97,7 @@ export class TimelineService extends BaseService {
|
|||||||
|
|
||||||
mapStack(entity?: Stack | null) {
|
mapStack(entity?: Stack | null) {
|
||||||
if (!entity) {
|
if (!entity) {
|
||||||
return;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -18,11 +18,11 @@ export type TimeBucketAssets = {
|
|||||||
isTrashed: number[];
|
isTrashed: number[];
|
||||||
isVideo: number[];
|
isVideo: number[];
|
||||||
isImage: number[];
|
isImage: number[];
|
||||||
thumbhash: (string | number)[];
|
thumbhash: (string | null)[];
|
||||||
localDateTime: Date[];
|
localDateTime: Date[];
|
||||||
stack: (TimelineStack | number)[];
|
stack: (TimelineStack | null)[];
|
||||||
duration: (string | number)[];
|
duration: (string | null)[];
|
||||||
projectionType: (string | number)[];
|
projectionType: (string | null)[];
|
||||||
livePhotoVideoId: (string | number)[];
|
livePhotoVideoId: (string | null)[];
|
||||||
description: AssetDescription[];
|
description: AssetDescription[];
|
||||||
};
|
};
|
||||||
|
@ -24,7 +24,10 @@ export function asHumanReadable(bytes: number, precision = 1): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if an asset is jsonified in the DB before being returned, its buffer fields will be hex-encoded strings
|
// if an asset is jsonified in the DB before being returned, its buffer fields will be hex-encoded strings
|
||||||
export const hexOrBufferToBase64 = (encoded: string | Buffer) => {
|
export const hexOrBufferToBase64 = (encoded: string | Buffer | null) => {
|
||||||
|
if (!encoded) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
if (typeof encoded === 'string') {
|
if (typeof encoded === 'string') {
|
||||||
return Buffer.from(encoded.slice(2), 'hex').toString('base64');
|
return Buffer.from(encoded.slice(2), 'hex').toString('base64');
|
||||||
}
|
}
|
||||||
|
@ -409,10 +409,6 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const assetOnFocusHandler = (asset: TimelineAsset) => {
|
|
||||||
assetInteraction.focussedAssetId = asset.id;
|
|
||||||
};
|
|
||||||
|
|
||||||
let isTrashEnabled = $derived($featureFlags.loaded && $featureFlags.trash);
|
let isTrashEnabled = $derived($featureFlags.loaded && $featureFlags.trash);
|
||||||
let idsSelectedAssets = $derived(assetInteraction.selectedAssets.map(({ id }) => id));
|
let idsSelectedAssets = $derived(assetInteraction.selectedAssets.map(({ id }) => id));
|
||||||
|
|
||||||
@ -481,7 +477,6 @@
|
|||||||
}}
|
}}
|
||||||
onSelect={() => handleSelectAssets(toTimelineAsset(asset))}
|
onSelect={() => handleSelectAssets(toTimelineAsset(asset))}
|
||||||
onMouseEvent={() => assetMouseEventHandler(toTimelineAsset(asset))}
|
onMouseEvent={() => assetMouseEventHandler(toTimelineAsset(asset))}
|
||||||
handleFocus={() => assetOnFocusHandler(toTimelineAsset(asset))}
|
|
||||||
{showArchiveIcon}
|
{showArchiveIcon}
|
||||||
asset={toTimelineAsset(asset)}
|
asset={toTimelineAsset(asset)}
|
||||||
selected={assetInteraction.hasSelectedAsset(asset.id)}
|
selected={assetInteraction.hasSelectedAsset(asset.id)}
|
||||||
|
@ -419,10 +419,6 @@ export class AssetBucket {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#decodeString(stringOrNumber: string | number) {
|
|
||||||
return typeof stringOrNumber === 'number' ? null : (stringOrNumber as string);
|
|
||||||
}
|
|
||||||
|
|
||||||
// note - if the assets are not part of this bucket, they will not be added
|
// note - if the assets are not part of this bucket, they will not be added
|
||||||
addAssets(bucketResponse: TimeBucketResponseDto) {
|
addAssets(bucketResponse: TimeBucketResponseDto) {
|
||||||
const addContext = new AddContext();
|
const addContext = new AddContext();
|
||||||
@ -432,20 +428,20 @@ export class AssetBucket {
|
|||||||
...bucketResponse.bucketAssets.description[i],
|
...bucketResponse.bucketAssets.description[i],
|
||||||
people: [],
|
people: [],
|
||||||
},
|
},
|
||||||
duration: this.#decodeString(bucketResponse.bucketAssets.duration[i]),
|
duration: bucketResponse.bucketAssets.duration[i],
|
||||||
id: bucketResponse.bucketAssets.id[i],
|
id: bucketResponse.bucketAssets.id[i],
|
||||||
isArchived: !!bucketResponse.bucketAssets.isArchived[i],
|
isArchived: !!bucketResponse.bucketAssets.isArchived[i],
|
||||||
isFavorite: !!bucketResponse.bucketAssets.isFavorite[i],
|
isFavorite: !!bucketResponse.bucketAssets.isFavorite[i],
|
||||||
isImage: !!bucketResponse.bucketAssets.isImage[i],
|
isImage: !!bucketResponse.bucketAssets.isImage[i],
|
||||||
isTrashed: !!bucketResponse.bucketAssets.isTrashed[i],
|
isTrashed: !!bucketResponse.bucketAssets.isTrashed[i],
|
||||||
isVideo: !!bucketResponse.bucketAssets.isVideo[i],
|
isVideo: !!bucketResponse.bucketAssets.isVideo[i],
|
||||||
livePhotoVideoId: this.#decodeString(bucketResponse.bucketAssets.livePhotoVideoId[i]),
|
livePhotoVideoId: bucketResponse.bucketAssets.livePhotoVideoId[i],
|
||||||
localDateTime: bucketResponse.bucketAssets.localDateTime[i],
|
localDateTime: bucketResponse.bucketAssets.localDateTime[i],
|
||||||
ownerId: bucketResponse.bucketAssets.ownerId[i],
|
ownerId: bucketResponse.bucketAssets.ownerId[i],
|
||||||
projectionType: this.#decodeString(bucketResponse.bucketAssets.projectionType[i]),
|
projectionType: bucketResponse.bucketAssets.projectionType[i],
|
||||||
ratio: bucketResponse.bucketAssets.ratio[i],
|
ratio: bucketResponse.bucketAssets.ratio[i],
|
||||||
stack: bucketResponse.bucketAssets.stack[i],
|
stack: bucketResponse.bucketAssets.stack[i],
|
||||||
thumbhash: this.#decodeString(bucketResponse.bucketAssets.thumbhash[i]),
|
thumbhash: bucketResponse.bucketAssets.thumbhash[i],
|
||||||
};
|
};
|
||||||
this.addTimelineAsset(timelineAsset, addContext);
|
this.addTimelineAsset(timelineAsset, addContext);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user