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)
|
||||
.get('/timeline/buckets')
|
||||
.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.body).toEqual(errorDto.badRequest());
|
||||
@ -112,7 +112,7 @@ describe('/timeline', () => {
|
||||
const req2 = await request(app)
|
||||
.get('/timeline/buckets')
|
||||
.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.body).toEqual(errorDto.badRequest());
|
||||
@ -122,7 +122,7 @@ describe('/timeline', () => {
|
||||
const req1 = await request(app)
|
||||
.get('/timeline/buckets')
|
||||
.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.body).toEqual(errorDto.badRequest());
|
||||
@ -130,7 +130,7 @@ describe('/timeline', () => {
|
||||
const req2 = await request(app)
|
||||
.get('/timeline/buckets')
|
||||
.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.body).toEqual(errorDto.badRequest());
|
||||
@ -140,7 +140,7 @@ describe('/timeline', () => {
|
||||
const req = await request(app)
|
||||
.get('/timeline/buckets')
|
||||
.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.body).toEqual(errorDto.badRequest());
|
||||
@ -150,7 +150,6 @@ describe('/timeline', () => {
|
||||
describe('GET /timeline/bucket', () => {
|
||||
it('should require authentication', async () => {
|
||||
const { status, body } = await request(app).get('/timeline/bucket').query({
|
||||
size: TimeBucketSize.Month,
|
||||
timeBucket: '1900-01-01',
|
||||
});
|
||||
|
||||
@ -161,7 +160,7 @@ describe('/timeline', () => {
|
||||
it('should handle 5 digit years', async () => {
|
||||
const { status, body } = await request(app)
|
||||
.get('/timeline/bucket')
|
||||
.query({ size: TimeBucketSize.Month, timeBucket: '012345-01-01' })
|
||||
.query({ timeBucket: '012345-01-01' })
|
||||
.set('Authorization', `Bearer ${timeBucketUser.accessToken}`);
|
||||
|
||||
expect(status).toBe(200);
|
||||
@ -173,7 +172,7 @@ describe('/timeline', () => {
|
||||
// const { status, body } = await request(app)
|
||||
// .get('/timeline/bucket')
|
||||
// .set('Authorization', `Bearer ${user.accessToken}`)
|
||||
// .query({ size: TimeBucketSize.Month, timeBucket: 'foo' });
|
||||
// .query({ timeBucket: 'foo' });
|
||||
|
||||
// expect(status).toBe(400);
|
||||
// expect(body).toEqual(errorDto.badRequest);
|
||||
@ -183,7 +182,7 @@ describe('/timeline', () => {
|
||||
const { status, body } = await request(app)
|
||||
.get('/timeline/bucket')
|
||||
.set('Authorization', `Bearer ${timeBucketUser.accessToken}`)
|
||||
.query({ size: TimeBucketSize.Month, timeBucket: '1970-02-10' });
|
||||
.query({ timeBucket: '1970-02-10' });
|
||||
|
||||
expect(status).toBe(200);
|
||||
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)
|
||||
- [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)
|
||||
|
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/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';
|
||||
|
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);
|
||||
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':
|
||||
|
@ -56,7 +56,7 @@ class TimeBucketAssetResponseDto {
|
||||
|
||||
List<num> ratio;
|
||||
|
||||
List<TimelineStackResponseDto> stack;
|
||||
List<TimeBucketAssetResponseDtoStackInner> stack;
|
||||
|
||||
List<TimeBucketAssetResponseDtoDurationInner> thumbhash;
|
||||
|
||||
@ -158,7 +158,7 @@ class TimeBucketAssetResponseDto {
|
||||
ratio: json[r'ratio'] is Iterable
|
||||
? (json[r'ratio'] as Iterable).cast<num>().toList(growable: false)
|
||||
: const [],
|
||||
stack: TimelineStackResponseDto.listFromJson(json[r'stack']),
|
||||
stack: TimeBucketAssetResponseDtoStackInner.listFromJson(json[r'stack']),
|
||||
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": "number"
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -13632,7 +13632,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "number"
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -13661,7 +13661,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "number"
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -13677,7 +13677,14 @@
|
||||
"stack": {
|
||||
"default": [],
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/TimelineStackResponseDto"
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "TimelineStackResponseDto"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
@ -13689,7 +13696,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "number"
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -13762,25 +13769,6 @@
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"TimelineStackResponseDto": {
|
||||
"properties": {
|
||||
"assetCount": {
|
||||
"type": "number"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"primaryAssetId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"assetCount",
|
||||
"id",
|
||||
"primaryAssetId"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"ToneMapping": {
|
||||
"enum": [
|
||||
"hable",
|
||||
|
@ -1388,27 +1388,22 @@ 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 | number)[];
|
||||
duration: (string | null)[];
|
||||
id: string[];
|
||||
isArchived: number[];
|
||||
isFavorite: number[];
|
||||
isImage: number[];
|
||||
isTrashed: number[];
|
||||
isVideo: number[];
|
||||
livePhotoVideoId: (string | number)[];
|
||||
livePhotoVideoId: (string | null)[];
|
||||
localDateTime: string[];
|
||||
ownerId: string[];
|
||||
projectionType: (string | number)[];
|
||||
projectionType: (string | null)[];
|
||||
ratio: number[];
|
||||
stack: TimelineStackResponseDto[];
|
||||
thumbhash: (string | number)[];
|
||||
stack: (any | null)[];
|
||||
thumbhash: (string | null)[];
|
||||
};
|
||||
export type TimeBucketResponseDto = {
|
||||
bucketAssets: TimeBucketAssetResponseDto;
|
||||
|
@ -72,7 +72,9 @@ class SqlGenerator {
|
||||
await rm(this.options.targetDir, { force: true, recursive: true });
|
||||
await mkdir(this.options.targetDir);
|
||||
|
||||
if (!process.env.DB_HOSTNAME) {
|
||||
process.env.DB_HOSTNAME = 'localhost';
|
||||
}
|
||||
const { database, cls, otel } = new ConfigRepository().getEnv();
|
||||
|
||||
const moduleFixture = await Test.createTestingModule({
|
||||
|
@ -183,7 +183,7 @@ export function mapAsset(entity: MapAsset, options: AssetMapOptions = {}): Asset
|
||||
tags: entity.tags?.map((tag) => mapTag(tag)),
|
||||
people: peopleWithFaces(entity.faces),
|
||||
unassignedFaces: entity.faces?.filter((face) => !face.person).map((a) => mapFacesWithoutPerson(a)),
|
||||
checksum: hexOrBufferToBase64(entity.checksum),
|
||||
checksum: hexOrBufferToBase64(entity.checksum)!,
|
||||
stack: withStack ? mapStack(entity) : undefined,
|
||||
isOffline: entity.isOffline,
|
||||
hasMetadata: true,
|
||||
|
@ -104,12 +104,12 @@ export class TimeBucketAssetResponseDto implements TimeBucketAssets {
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
type: 'number',
|
||||
type: 'null',
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
thumbhash: (string | number)[] = [];
|
||||
thumbhash: (string | null)[] = [];
|
||||
|
||||
@ApiProperty()
|
||||
localDateTime: Date[] = [];
|
||||
@ -122,15 +122,27 @@ export class TimeBucketAssetResponseDto implements TimeBucketAssets {
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
type: 'number',
|
||||
type: 'null',
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
duration: (string | number)[] = [];
|
||||
duration: (string | null)[] = [];
|
||||
|
||||
@ApiProperty({ type: [TimelineStackResponseDto] })
|
||||
stack: (TimelineStackResponseDto | number)[] = [];
|
||||
@ApiProperty({
|
||||
type: 'array',
|
||||
items: {
|
||||
oneOf: [
|
||||
{
|
||||
type: 'TimelineStackResponseDto',
|
||||
},
|
||||
{
|
||||
type: 'null',
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
stack: (TimelineStackResponseDto | null)[] = [];
|
||||
|
||||
@ApiProperty({
|
||||
type: 'array',
|
||||
@ -140,12 +152,12 @@ export class TimeBucketAssetResponseDto implements TimeBucketAssets {
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
type: 'number',
|
||||
type: 'null',
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
projectionType: (string | number)[] = [];
|
||||
projectionType: (string | null)[] = [];
|
||||
|
||||
@ApiProperty({
|
||||
type: 'array',
|
||||
@ -155,12 +167,12 @@ export class TimeBucketAssetResponseDto implements TimeBucketAssets {
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
type: 'number',
|
||||
type: 'null',
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
livePhotoVideoId: (string | number)[] = [];
|
||||
livePhotoVideoId: (string | null)[] = [];
|
||||
|
||||
@ApiProperty()
|
||||
description: TimelineAssetDescriptionDto[] = [];
|
||||
|
@ -255,8 +255,23 @@ order by
|
||||
|
||||
-- AssetRepository.getTimeBucket
|
||||
select
|
||||
"assets".*,
|
||||
to_json("exif") as "exifInfo",
|
||||
"assets"."id" as "id",
|
||||
"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"
|
||||
from
|
||||
"assets"
|
||||
|
@ -142,7 +142,7 @@ export class SyncService extends BaseService {
|
||||
updateId,
|
||||
data: {
|
||||
...data,
|
||||
checksum: hexOrBufferToBase64(checksum),
|
||||
checksum: hexOrBufferToBase64(checksum)!,
|
||||
thumbhash: thumbhash ? hexOrBufferToBase64(thumbhash) : null,
|
||||
},
|
||||
}),
|
||||
@ -172,7 +172,7 @@ export class SyncService extends BaseService {
|
||||
updateId,
|
||||
data: {
|
||||
...data,
|
||||
checksum: hexOrBufferToBase64(checksum),
|
||||
checksum: hexOrBufferToBase64(checksum)!,
|
||||
thumbhash: thumbhash ? hexOrBufferToBase64(thumbhash) : null,
|
||||
},
|
||||
}),
|
||||
|
@ -75,12 +75,12 @@ export class TimelineService extends BaseService {
|
||||
bucketAssets.isArchived.push(item.isArchived ? 1 : 0);
|
||||
bucketAssets.isFavorite.push(item.isFavorite ? 1 : 0);
|
||||
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.stack.push(this.mapStack(item.stack) || 0);
|
||||
bucketAssets.duration.push(item.duration || 0);
|
||||
bucketAssets.projectionType.push(item.projectionType || 0);
|
||||
bucketAssets.livePhotoVideoId.push(item.livePhotoVideoId || 0);
|
||||
bucketAssets.stack.push(this.mapStack(item.stack));
|
||||
bucketAssets.duration.push(item.duration);
|
||||
bucketAssets.projectionType.push(item.projectionType);
|
||||
bucketAssets.livePhotoVideoId.push(item.livePhotoVideoId);
|
||||
bucketAssets.isImage.push(item.type === AssetType.IMAGE ? 1 : 0);
|
||||
bucketAssets.isVideo.push(item.type === AssetType.VIDEO ? 1 : 0);
|
||||
bucketAssets.description.push({
|
||||
@ -97,7 +97,7 @@ export class TimelineService extends BaseService {
|
||||
|
||||
mapStack(entity?: Stack | null) {
|
||||
if (!entity) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
|
@ -18,11 +18,11 @@ export type TimeBucketAssets = {
|
||||
isTrashed: number[];
|
||||
isVideo: number[];
|
||||
isImage: number[];
|
||||
thumbhash: (string | number)[];
|
||||
thumbhash: (string | null)[];
|
||||
localDateTime: Date[];
|
||||
stack: (TimelineStack | number)[];
|
||||
duration: (string | number)[];
|
||||
projectionType: (string | number)[];
|
||||
livePhotoVideoId: (string | number)[];
|
||||
stack: (TimelineStack | null)[];
|
||||
duration: (string | null)[];
|
||||
projectionType: (string | null)[];
|
||||
livePhotoVideoId: (string | null)[];
|
||||
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
|
||||
export const hexOrBufferToBase64 = (encoded: string | Buffer) => {
|
||||
export const hexOrBufferToBase64 = (encoded: string | Buffer | null) => {
|
||||
if (!encoded) {
|
||||
return null;
|
||||
}
|
||||
if (typeof encoded === 'string') {
|
||||
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 idsSelectedAssets = $derived(assetInteraction.selectedAssets.map(({ id }) => id));
|
||||
|
||||
@ -481,7 +477,6 @@
|
||||
}}
|
||||
onSelect={() => handleSelectAssets(toTimelineAsset(asset))}
|
||||
onMouseEvent={() => assetMouseEventHandler(toTimelineAsset(asset))}
|
||||
handleFocus={() => assetOnFocusHandler(toTimelineAsset(asset))}
|
||||
{showArchiveIcon}
|
||||
asset={toTimelineAsset(asset)}
|
||||
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
|
||||
addAssets(bucketResponse: TimeBucketResponseDto) {
|
||||
const addContext = new AddContext();
|
||||
@ -432,20 +428,20 @@ export class AssetBucket {
|
||||
...bucketResponse.bucketAssets.description[i],
|
||||
people: [],
|
||||
},
|
||||
duration: this.#decodeString(bucketResponse.bucketAssets.duration[i]),
|
||||
duration: bucketResponse.bucketAssets.duration[i],
|
||||
id: bucketResponse.bucketAssets.id[i],
|
||||
isArchived: !!bucketResponse.bucketAssets.isArchived[i],
|
||||
isFavorite: !!bucketResponse.bucketAssets.isFavorite[i],
|
||||
isImage: !!bucketResponse.bucketAssets.isImage[i],
|
||||
isTrashed: !!bucketResponse.bucketAssets.isTrashed[i],
|
||||
isVideo: !!bucketResponse.bucketAssets.isVideo[i],
|
||||
livePhotoVideoId: this.#decodeString(bucketResponse.bucketAssets.livePhotoVideoId[i]),
|
||||
livePhotoVideoId: bucketResponse.bucketAssets.livePhotoVideoId[i],
|
||||
localDateTime: bucketResponse.bucketAssets.localDateTime[i],
|
||||
ownerId: bucketResponse.bucketAssets.ownerId[i],
|
||||
projectionType: this.#decodeString(bucketResponse.bucketAssets.projectionType[i]),
|
||||
projectionType: bucketResponse.bucketAssets.projectionType[i],
|
||||
ratio: bucketResponse.bucketAssets.ratio[i],
|
||||
stack: bucketResponse.bucketAssets.stack[i],
|
||||
thumbhash: this.#decodeString(bucketResponse.bucketAssets.thumbhash[i]),
|
||||
thumbhash: bucketResponse.bucketAssets.thumbhash[i],
|
||||
};
|
||||
this.addTimelineAsset(timelineAsset, addContext);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user