refactor(server): decouple generated images from image formats (#8246)

* rename

thumbnail config

update target paths, fix tests

rename to image settings

replace legacy enum

better typing

update sql

update api

remove config option

fix

* update docs

* update other thumbnail configs in migration

* keep legacy enum for now

* fix jumbled job names

* fix jumbled job names in tests

* rename thumbhash job

* rename dto

* fix tests

* preserve order

* remove unused import

* keep old fields in dto, marked deprecated

* update sql

---------

Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
This commit is contained in:
Mert 2024-04-02 00:56:56 -04:00 committed by GitHub
parent e520c0d1f5
commit 8edc2fb46f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
66 changed files with 916 additions and 547 deletions

View file

@ -0,0 +1,85 @@
//
// AUTO-GENERATED FILE, DO NOT MODIFY!
//
// @dart=2.12
// 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 ImageFormat {
/// Instantiate a new enum with the provided [value].
const ImageFormat._(this.value);
/// The underlying value of this enum member.
final String value;
@override
String toString() => value;
String toJson() => value;
static const jpeg = ImageFormat._(r'jpeg');
static const webp = ImageFormat._(r'webp');
/// List of all possible values in this [enum][ImageFormat].
static const values = <ImageFormat>[
jpeg,
webp,
];
static ImageFormat? fromJson(dynamic value) => ImageFormatTypeTransformer().decode(value);
static List<ImageFormat> listFromJson(dynamic json, {bool growable = false,}) {
final result = <ImageFormat>[];
if (json is List && json.isNotEmpty) {
for (final row in json) {
final value = ImageFormat.fromJson(row);
if (value != null) {
result.add(value);
}
}
}
return result.toList(growable: growable);
}
}
/// Transformation class that can [encode] an instance of [ImageFormat] to String,
/// and [decode] dynamic data back to [ImageFormat].
class ImageFormatTypeTransformer {
factory ImageFormatTypeTransformer() => _instance ??= const ImageFormatTypeTransformer._();
const ImageFormatTypeTransformer._();
String encode(ImageFormat data) => data.value;
/// Decodes a [dynamic value][data] to a ImageFormat.
///
/// If [allowNull] is true and the [dynamic value][data] cannot be decoded successfully,
/// then null is returned. However, if [allowNull] is false and the [dynamic value][data]
/// cannot be decoded successfully, then an [UnimplementedError] is thrown.
///
/// The [allowNull] is very handy when an API changes and a new enum value is added or removed,
/// and users are still using an old app with the old code.
ImageFormat? decode(dynamic data, {bool allowNull = true}) {
if (data != null) {
switch (data) {
case r'jpeg': return ImageFormat.jpeg;
case r'webp': return ImageFormat.webp;
default:
if (!allowNull) {
throw ArgumentError('Unknown enum value to decode: $data');
}
}
}
return null;
}
/// Singleton [ImageFormatTypeTransformer] instance.
static ImageFormatTypeTransformer? _instance;
}

View file

@ -40,11 +40,13 @@ class MetadataSearchDto {
this.originalPath,
this.page,
this.personIds = const [],
this.previewPath,
this.resizePath,
this.size,
this.state,
this.takenAfter,
this.takenBefore,
this.thumbnailPath,
this.trashedAfter,
this.trashedBefore,
this.type,
@ -268,6 +270,14 @@ class MetadataSearchDto {
List<String> personIds;
///
/// Please note: This property should have been non-nullable! Since the specification file
/// does not include a default value (using the "default:" property), however, the generated
/// source code must fall back to having a nullable type.
/// Consider adding a "default:" property in the specification file to hide this note.
///
String? previewPath;
///
/// Please note: This property should have been non-nullable! Since the specification file
/// does not include a default value (using the "default:" property), however, the generated
@ -308,6 +318,14 @@ class MetadataSearchDto {
///
DateTime? takenBefore;
///
/// Please note: This property should have been non-nullable! Since the specification file
/// does not include a default value (using the "default:" property), however, the generated
/// source code must fall back to having a nullable type.
/// Consider adding a "default:" property in the specification file to hide this note.
///
String? thumbnailPath;
///
/// Please note: This property should have been non-nullable! Since the specification file
/// does not include a default value (using the "default:" property), however, the generated
@ -419,11 +437,13 @@ class MetadataSearchDto {
other.originalPath == originalPath &&
other.page == page &&
_deepEquality.equals(other.personIds, personIds) &&
other.previewPath == previewPath &&
other.resizePath == resizePath &&
other.size == size &&
other.state == state &&
other.takenAfter == takenAfter &&
other.takenBefore == takenBefore &&
other.thumbnailPath == thumbnailPath &&
other.trashedAfter == trashedAfter &&
other.trashedBefore == trashedBefore &&
other.type == type &&
@ -466,11 +486,13 @@ class MetadataSearchDto {
(originalPath == null ? 0 : originalPath!.hashCode) +
(page == null ? 0 : page!.hashCode) +
(personIds.hashCode) +
(previewPath == null ? 0 : previewPath!.hashCode) +
(resizePath == null ? 0 : resizePath!.hashCode) +
(size == null ? 0 : size!.hashCode) +
(state == null ? 0 : state!.hashCode) +
(takenAfter == null ? 0 : takenAfter!.hashCode) +
(takenBefore == null ? 0 : takenBefore!.hashCode) +
(thumbnailPath == null ? 0 : thumbnailPath!.hashCode) +
(trashedAfter == null ? 0 : trashedAfter!.hashCode) +
(trashedBefore == null ? 0 : trashedBefore!.hashCode) +
(type == null ? 0 : type!.hashCode) +
@ -484,7 +506,7 @@ class MetadataSearchDto {
(withStacked == null ? 0 : withStacked!.hashCode);
@override
String toString() => 'MetadataSearchDto[checksum=$checksum, city=$city, country=$country, createdAfter=$createdAfter, createdBefore=$createdBefore, deviceAssetId=$deviceAssetId, deviceId=$deviceId, encodedVideoPath=$encodedVideoPath, id=$id, isArchived=$isArchived, isEncoded=$isEncoded, isExternal=$isExternal, isFavorite=$isFavorite, isMotion=$isMotion, isNotInAlbum=$isNotInAlbum, isOffline=$isOffline, isReadOnly=$isReadOnly, isVisible=$isVisible, lensModel=$lensModel, libraryId=$libraryId, make=$make, model=$model, order=$order, originalFileName=$originalFileName, originalPath=$originalPath, page=$page, personIds=$personIds, resizePath=$resizePath, size=$size, state=$state, takenAfter=$takenAfter, takenBefore=$takenBefore, trashedAfter=$trashedAfter, trashedBefore=$trashedBefore, type=$type, updatedAfter=$updatedAfter, updatedBefore=$updatedBefore, webpPath=$webpPath, withArchived=$withArchived, withDeleted=$withDeleted, withExif=$withExif, withPeople=$withPeople, withStacked=$withStacked]';
String toString() => 'MetadataSearchDto[checksum=$checksum, city=$city, country=$country, createdAfter=$createdAfter, createdBefore=$createdBefore, deviceAssetId=$deviceAssetId, deviceId=$deviceId, encodedVideoPath=$encodedVideoPath, id=$id, isArchived=$isArchived, isEncoded=$isEncoded, isExternal=$isExternal, isFavorite=$isFavorite, isMotion=$isMotion, isNotInAlbum=$isNotInAlbum, isOffline=$isOffline, isReadOnly=$isReadOnly, isVisible=$isVisible, lensModel=$lensModel, libraryId=$libraryId, make=$make, model=$model, order=$order, originalFileName=$originalFileName, originalPath=$originalPath, page=$page, personIds=$personIds, previewPath=$previewPath, resizePath=$resizePath, size=$size, state=$state, takenAfter=$takenAfter, takenBefore=$takenBefore, thumbnailPath=$thumbnailPath, trashedAfter=$trashedAfter, trashedBefore=$trashedBefore, type=$type, updatedAfter=$updatedAfter, updatedBefore=$updatedBefore, webpPath=$webpPath, withArchived=$withArchived, withDeleted=$withDeleted, withExif=$withExif, withPeople=$withPeople, withStacked=$withStacked]';
Map<String, dynamic> toJson() {
final json = <String, dynamic>{};
@ -619,6 +641,11 @@ class MetadataSearchDto {
// json[r'page'] = null;
}
json[r'personIds'] = this.personIds;
if (this.previewPath != null) {
json[r'previewPath'] = this.previewPath;
} else {
// json[r'previewPath'] = null;
}
if (this.resizePath != null) {
json[r'resizePath'] = this.resizePath;
} else {
@ -644,6 +671,11 @@ class MetadataSearchDto {
} else {
// json[r'takenBefore'] = null;
}
if (this.thumbnailPath != null) {
json[r'thumbnailPath'] = this.thumbnailPath;
} else {
// json[r'thumbnailPath'] = null;
}
if (this.trashedAfter != null) {
json[r'trashedAfter'] = this.trashedAfter!.toUtc().toIso8601String();
} else {
@ -735,11 +767,13 @@ class MetadataSearchDto {
personIds: json[r'personIds'] is Iterable
? (json[r'personIds'] as Iterable).cast<String>().toList(growable: false)
: const [],
previewPath: mapValueOfType<String>(json, r'previewPath'),
resizePath: mapValueOfType<String>(json, r'resizePath'),
size: num.parse('${json[r'size']}'),
state: mapValueOfType<String>(json, r'state'),
takenAfter: mapDateTime(json, r'takenAfter', r''),
takenBefore: mapDateTime(json, r'takenBefore', r''),
thumbnailPath: mapValueOfType<String>(json, r'thumbnailPath'),
trashedAfter: mapDateTime(json, r'trashedAfter', r''),
trashedBefore: mapDateTime(json, r'trashedBefore', r''),
type: AssetTypeEnum.fromJson(json[r'type']),

View file

@ -24,8 +24,8 @@ class PathType {
String toJson() => value;
static const original = PathType._(r'original');
static const jpegThumbnail = PathType._(r'jpeg_thumbnail');
static const webpThumbnail = PathType._(r'webp_thumbnail');
static const preview = PathType._(r'preview');
static const thumbnail = PathType._(r'thumbnail');
static const encodedVideo = PathType._(r'encoded_video');
static const sidecar = PathType._(r'sidecar');
static const face = PathType._(r'face');
@ -34,8 +34,8 @@ class PathType {
/// List of all possible values in this [enum][PathType].
static const values = <PathType>[
original,
jpegThumbnail,
webpThumbnail,
preview,
thumbnail,
encodedVideo,
sidecar,
face,
@ -79,8 +79,8 @@ class PathTypeTypeTransformer {
if (data != null) {
switch (data) {
case r'original': return PathType.original;
case r'jpeg_thumbnail': return PathType.jpegThumbnail;
case r'webp_thumbnail': return PathType.webpThumbnail;
case r'preview': return PathType.preview;
case r'thumbnail': return PathType.thumbnail;
case r'encoded_video': return PathType.encodedVideo;
case r'sidecar': return PathType.sidecar;
case r'face': return PathType.face;

View file

@ -14,6 +14,7 @@ class SystemConfigDto {
/// Returns a new [SystemConfigDto] instance.
SystemConfigDto({
required this.ffmpeg,
required this.image,
required this.job,
required this.library_,
required this.logging,
@ -26,13 +27,14 @@ class SystemConfigDto {
required this.server,
required this.storageTemplate,
required this.theme,
required this.thumbnail,
required this.trash,
required this.user,
});
SystemConfigFFmpegDto ffmpeg;
SystemConfigImageDto image;
SystemConfigJobDto job;
SystemConfigLibraryDto library_;
@ -57,8 +59,6 @@ class SystemConfigDto {
SystemConfigThemeDto theme;
SystemConfigThumbnailDto thumbnail;
SystemConfigTrashDto trash;
SystemConfigUserDto user;
@ -66,6 +66,7 @@ class SystemConfigDto {
@override
bool operator ==(Object other) => identical(this, other) || other is SystemConfigDto &&
other.ffmpeg == ffmpeg &&
other.image == image &&
other.job == job &&
other.library_ == library_ &&
other.logging == logging &&
@ -78,7 +79,6 @@ class SystemConfigDto {
other.server == server &&
other.storageTemplate == storageTemplate &&
other.theme == theme &&
other.thumbnail == thumbnail &&
other.trash == trash &&
other.user == user;
@ -86,6 +86,7 @@ class SystemConfigDto {
int get hashCode =>
// ignore: unnecessary_parenthesis
(ffmpeg.hashCode) +
(image.hashCode) +
(job.hashCode) +
(library_.hashCode) +
(logging.hashCode) +
@ -98,16 +99,16 @@ class SystemConfigDto {
(server.hashCode) +
(storageTemplate.hashCode) +
(theme.hashCode) +
(thumbnail.hashCode) +
(trash.hashCode) +
(user.hashCode);
@override
String toString() => 'SystemConfigDto[ffmpeg=$ffmpeg, job=$job, library_=$library_, logging=$logging, machineLearning=$machineLearning, map=$map, newVersionCheck=$newVersionCheck, oauth=$oauth, passwordLogin=$passwordLogin, reverseGeocoding=$reverseGeocoding, server=$server, storageTemplate=$storageTemplate, theme=$theme, thumbnail=$thumbnail, trash=$trash, user=$user]';
String toString() => 'SystemConfigDto[ffmpeg=$ffmpeg, image=$image, job=$job, library_=$library_, logging=$logging, machineLearning=$machineLearning, map=$map, newVersionCheck=$newVersionCheck, oauth=$oauth, passwordLogin=$passwordLogin, reverseGeocoding=$reverseGeocoding, server=$server, storageTemplate=$storageTemplate, theme=$theme, trash=$trash, user=$user]';
Map<String, dynamic> toJson() {
final json = <String, dynamic>{};
json[r'ffmpeg'] = this.ffmpeg;
json[r'image'] = this.image;
json[r'job'] = this.job;
json[r'library'] = this.library_;
json[r'logging'] = this.logging;
@ -120,7 +121,6 @@ class SystemConfigDto {
json[r'server'] = this.server;
json[r'storageTemplate'] = this.storageTemplate;
json[r'theme'] = this.theme;
json[r'thumbnail'] = this.thumbnail;
json[r'trash'] = this.trash;
json[r'user'] = this.user;
return json;
@ -135,6 +135,7 @@ class SystemConfigDto {
return SystemConfigDto(
ffmpeg: SystemConfigFFmpegDto.fromJson(json[r'ffmpeg'])!,
image: SystemConfigImageDto.fromJson(json[r'image'])!,
job: SystemConfigJobDto.fromJson(json[r'job'])!,
library_: SystemConfigLibraryDto.fromJson(json[r'library'])!,
logging: SystemConfigLoggingDto.fromJson(json[r'logging'])!,
@ -147,7 +148,6 @@ class SystemConfigDto {
server: SystemConfigServerDto.fromJson(json[r'server'])!,
storageTemplate: SystemConfigStorageTemplateDto.fromJson(json[r'storageTemplate'])!,
theme: SystemConfigThemeDto.fromJson(json[r'theme'])!,
thumbnail: SystemConfigThumbnailDto.fromJson(json[r'thumbnail'])!,
trash: SystemConfigTrashDto.fromJson(json[r'trash'])!,
user: SystemConfigUserDto.fromJson(json[r'user'])!,
);
@ -198,6 +198,7 @@ class SystemConfigDto {
/// The list of required keys that must be present in a JSON.
static const requiredKeys = <String>{
'ffmpeg',
'image',
'job',
'library',
'logging',
@ -210,7 +211,6 @@ class SystemConfigDto {
'server',
'storageTemplate',
'theme',
'thumbnail',
'trash',
'user',
};

View file

@ -0,0 +1,138 @@
//
// AUTO-GENERATED FILE, DO NOT MODIFY!
//
// @dart=2.12
// 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 SystemConfigImageDto {
/// Returns a new [SystemConfigImageDto] instance.
SystemConfigImageDto({
required this.colorspace,
required this.previewFormat,
required this.previewSize,
required this.quality,
required this.thumbnailFormat,
required this.thumbnailSize,
});
Colorspace colorspace;
ImageFormat previewFormat;
int previewSize;
int quality;
ImageFormat thumbnailFormat;
int thumbnailSize;
@override
bool operator ==(Object other) => identical(this, other) || other is SystemConfigImageDto &&
other.colorspace == colorspace &&
other.previewFormat == previewFormat &&
other.previewSize == previewSize &&
other.quality == quality &&
other.thumbnailFormat == thumbnailFormat &&
other.thumbnailSize == thumbnailSize;
@override
int get hashCode =>
// ignore: unnecessary_parenthesis
(colorspace.hashCode) +
(previewFormat.hashCode) +
(previewSize.hashCode) +
(quality.hashCode) +
(thumbnailFormat.hashCode) +
(thumbnailSize.hashCode);
@override
String toString() => 'SystemConfigImageDto[colorspace=$colorspace, previewFormat=$previewFormat, previewSize=$previewSize, quality=$quality, thumbnailFormat=$thumbnailFormat, thumbnailSize=$thumbnailSize]';
Map<String, dynamic> toJson() {
final json = <String, dynamic>{};
json[r'colorspace'] = this.colorspace;
json[r'previewFormat'] = this.previewFormat;
json[r'previewSize'] = this.previewSize;
json[r'quality'] = this.quality;
json[r'thumbnailFormat'] = this.thumbnailFormat;
json[r'thumbnailSize'] = this.thumbnailSize;
return json;
}
/// Returns a new [SystemConfigImageDto] instance and imports its values from
/// [value] if it's a [Map], null otherwise.
// ignore: prefer_constructors_over_static_methods
static SystemConfigImageDto? fromJson(dynamic value) {
if (value is Map) {
final json = value.cast<String, dynamic>();
return SystemConfigImageDto(
colorspace: Colorspace.fromJson(json[r'colorspace'])!,
previewFormat: ImageFormat.fromJson(json[r'previewFormat'])!,
previewSize: mapValueOfType<int>(json, r'previewSize')!,
quality: mapValueOfType<int>(json, r'quality')!,
thumbnailFormat: ImageFormat.fromJson(json[r'thumbnailFormat'])!,
thumbnailSize: mapValueOfType<int>(json, r'thumbnailSize')!,
);
}
return null;
}
static List<SystemConfigImageDto> listFromJson(dynamic json, {bool growable = false,}) {
final result = <SystemConfigImageDto>[];
if (json is List && json.isNotEmpty) {
for (final row in json) {
final value = SystemConfigImageDto.fromJson(row);
if (value != null) {
result.add(value);
}
}
}
return result.toList(growable: growable);
}
static Map<String, SystemConfigImageDto> mapFromJson(dynamic json) {
final map = <String, SystemConfigImageDto>{};
if (json is Map && json.isNotEmpty) {
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
for (final entry in json.entries) {
final value = SystemConfigImageDto.fromJson(entry.value);
if (value != null) {
map[entry.key] = value;
}
}
}
return map;
}
// maps a json object with a list of SystemConfigImageDto-objects as value to a dart map
static Map<String, List<SystemConfigImageDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
final map = <String, List<SystemConfigImageDto>>{};
if (json is Map && json.isNotEmpty) {
// ignore: parameter_assignments
json = json.cast<String, dynamic>();
for (final entry in json.entries) {
map[entry.key] = SystemConfigImageDto.listFromJson(entry.value, growable: growable,);
}
}
return map;
}
/// The list of required keys that must be present in a JSON.
static const requiredKeys = <String>{
'colorspace',
'previewFormat',
'previewSize',
'quality',
'thumbnailFormat',
'thumbnailSize',
};
}

View file

@ -1,122 +0,0 @@
//
// AUTO-GENERATED FILE, DO NOT MODIFY!
//
// @dart=2.12
// 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 SystemConfigThumbnailDto {
/// Returns a new [SystemConfigThumbnailDto] instance.
SystemConfigThumbnailDto({
required this.colorspace,
required this.jpegSize,
required this.quality,
required this.webpSize,
});
Colorspace colorspace;
int jpegSize;
int quality;
int webpSize;
@override
bool operator ==(Object other) => identical(this, other) || other is SystemConfigThumbnailDto &&
other.colorspace == colorspace &&
other.jpegSize == jpegSize &&
other.quality == quality &&
other.webpSize == webpSize;
@override
int get hashCode =>
// ignore: unnecessary_parenthesis
(colorspace.hashCode) +
(jpegSize.hashCode) +
(quality.hashCode) +
(webpSize.hashCode);
@override
String toString() => 'SystemConfigThumbnailDto[colorspace=$colorspace, jpegSize=$jpegSize, quality=$quality, webpSize=$webpSize]';
Map<String, dynamic> toJson() {
final json = <String, dynamic>{};
json[r'colorspace'] = this.colorspace;
json[r'jpegSize'] = this.jpegSize;
json[r'quality'] = this.quality;
json[r'webpSize'] = this.webpSize;
return json;
}
/// Returns a new [SystemConfigThumbnailDto] instance and imports its values from
/// [value] if it's a [Map], null otherwise.
// ignore: prefer_constructors_over_static_methods
static SystemConfigThumbnailDto? fromJson(dynamic value) {
if (value is Map) {
final json = value.cast<String, dynamic>();
return SystemConfigThumbnailDto(
colorspace: Colorspace.fromJson(json[r'colorspace'])!,
jpegSize: mapValueOfType<int>(json, r'jpegSize')!,
quality: mapValueOfType<int>(json, r'quality')!,
webpSize: mapValueOfType<int>(json, r'webpSize')!,
);
}
return null;
}
static List<SystemConfigThumbnailDto> listFromJson(dynamic json, {bool growable = false,}) {
final result = <SystemConfigThumbnailDto>[];
if (json is List && json.isNotEmpty) {
for (final row in json) {
final value = SystemConfigThumbnailDto.fromJson(row);
if (value != null) {
result.add(value);
}
}
}
return result.toList(growable: growable);
}
static Map<String, SystemConfigThumbnailDto> mapFromJson(dynamic json) {
final map = <String, SystemConfigThumbnailDto>{};
if (json is Map && json.isNotEmpty) {
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
for (final entry in json.entries) {
final value = SystemConfigThumbnailDto.fromJson(entry.value);
if (value != null) {
map[entry.key] = value;
}
}
}
return map;
}
// maps a json object with a list of SystemConfigThumbnailDto-objects as value to a dart map
static Map<String, List<SystemConfigThumbnailDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
final map = <String, List<SystemConfigThumbnailDto>>{};
if (json is Map && json.isNotEmpty) {
// ignore: parameter_assignments
json = json.cast<String, dynamic>();
for (final entry in json.entries) {
map[entry.key] = SystemConfigThumbnailDto.listFromJson(entry.value, growable: growable,);
}
}
return map;
}
/// The list of required keys that must be present in a JSON.
static const requiredKeys = <String>{
'colorspace',
'jpegSize',
'quality',
'webpSize',
};
}