mirror of
https://github.com/immich-app/immich.git
synced 2025-12-28 09:14:59 +03:00
refactor: exif entity (#16621)
Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
This commit is contained in:
@@ -1,13 +1,16 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:immich_mobile/entities/exif_info.entity.dart';
|
||||
import 'package:immich_mobile/domain/models/exif.model.dart';
|
||||
import 'package:immich_mobile/extensions/string_extensions.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/exif.entity.dart'
|
||||
as entity;
|
||||
import 'package:immich_mobile/infrastructure/utils/exif.converter.dart';
|
||||
import 'package:immich_mobile/utils/hash.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
import 'package:photo_manager/photo_manager.dart' show AssetEntity;
|
||||
import 'package:immich_mobile/extensions/string_extensions.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:photo_manager/photo_manager.dart' show AssetEntity;
|
||||
|
||||
part 'asset.entity.g.dart';
|
||||
|
||||
@@ -27,8 +30,9 @@ class Asset {
|
||||
width = remote.exifInfo?.exifImageWidth?.toInt(),
|
||||
livePhotoVideoId = remote.livePhotoVideoId,
|
||||
ownerId = fastHash(remote.ownerId),
|
||||
exifInfo =
|
||||
remote.exifInfo != null ? ExifInfo.fromDto(remote.exifInfo!) : null,
|
||||
exifInfo = remote.exifInfo == null
|
||||
? null
|
||||
: ExifDtoConverter.fromDto(remote.exifInfo!),
|
||||
isFavorite = remote.isFavorite,
|
||||
isArchived = remote.isArchived,
|
||||
isTrashed = remote.isTrashed,
|
||||
@@ -359,14 +363,14 @@ class Asset {
|
||||
localId: localId,
|
||||
width: a.width ?? width,
|
||||
height: a.height ?? height,
|
||||
exifInfo: a.exifInfo?.copyWith(id: id) ?? exifInfo,
|
||||
exifInfo: a.exifInfo?.copyWith(assetId: id) ?? exifInfo,
|
||||
);
|
||||
} else if (isRemote) {
|
||||
return _copyWith(
|
||||
localId: localId ?? a.localId,
|
||||
width: width ?? a.width,
|
||||
height: height ?? a.height,
|
||||
exifInfo: exifInfo ?? a.exifInfo?.copyWith(id: id),
|
||||
exifInfo: exifInfo ?? a.exifInfo?.copyWith(assetId: id),
|
||||
);
|
||||
} else {
|
||||
// TODO: Revisit this and remove all bool field assignments
|
||||
@@ -407,7 +411,7 @@ class Asset {
|
||||
isArchived: a.isArchived,
|
||||
isTrashed: a.isTrashed,
|
||||
isOffline: a.isOffline,
|
||||
exifInfo: a.exifInfo?.copyWith(id: id) ?? exifInfo,
|
||||
exifInfo: a.exifInfo?.copyWith(assetId: id) ?? exifInfo,
|
||||
thumbhash: a.thumbhash,
|
||||
);
|
||||
} else {
|
||||
@@ -416,7 +420,8 @@ class Asset {
|
||||
localId: localId ?? a.localId,
|
||||
width: width ?? a.width,
|
||||
height: height ?? a.height,
|
||||
exifInfo: exifInfo ?? a.exifInfo?.copyWith(id: id),
|
||||
exifInfo: exifInfo ??
|
||||
a.exifInfo?.copyWith(assetId: id), // updated to use assetId
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -476,8 +481,8 @@ class Asset {
|
||||
Future<void> put(Isar db) async {
|
||||
await db.assets.put(this);
|
||||
if (exifInfo != null) {
|
||||
exifInfo!.id = id;
|
||||
await db.exifInfos.put(exifInfo!);
|
||||
await db.exifInfos
|
||||
.put(entity.ExifInfo.fromDto(exifInfo!.copyWith(assetId: id)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,241 +0,0 @@
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:openapi/api.dart';
|
||||
|
||||
part 'exif_info.entity.g.dart';
|
||||
|
||||
/// Exif information 1:1 relation with Asset
|
||||
@Collection(inheritance: false)
|
||||
class ExifInfo {
|
||||
Id? id;
|
||||
int? fileSize;
|
||||
DateTime? dateTimeOriginal;
|
||||
String? timeZone;
|
||||
String? make;
|
||||
String? model;
|
||||
String? lens;
|
||||
float? f;
|
||||
float? mm;
|
||||
short? iso;
|
||||
float? exposureSeconds;
|
||||
float? lat;
|
||||
float? long;
|
||||
String? city;
|
||||
String? state;
|
||||
String? country;
|
||||
String? description;
|
||||
String? orientation;
|
||||
|
||||
@ignore
|
||||
bool get hasCoordinates =>
|
||||
latitude != null && longitude != null && latitude != 0 && longitude != 0;
|
||||
|
||||
@ignore
|
||||
String get exposureTime {
|
||||
if (exposureSeconds == null) {
|
||||
return "";
|
||||
} else if (exposureSeconds! < 1) {
|
||||
return "1/${(1.0 / exposureSeconds!).round()} s";
|
||||
} else {
|
||||
return "${exposureSeconds!.toStringAsFixed(1)} s";
|
||||
}
|
||||
}
|
||||
|
||||
@ignore
|
||||
String get fNumber => f != null ? f!.toStringAsFixed(1) : "";
|
||||
|
||||
@ignore
|
||||
String get focalLength => mm != null ? mm!.toStringAsFixed(1) : "";
|
||||
|
||||
@ignore
|
||||
bool? _isFlipped;
|
||||
|
||||
@ignore
|
||||
@pragma('vm:prefer-inline')
|
||||
bool get isFlipped => _isFlipped ??= _isOrientationFlipped(orientation);
|
||||
|
||||
@ignore
|
||||
double? get latitude => lat;
|
||||
|
||||
@ignore
|
||||
double? get longitude => long;
|
||||
|
||||
ExifInfo.fromDto(ExifResponseDto dto)
|
||||
: fileSize = dto.fileSizeInByte,
|
||||
dateTimeOriginal = dto.dateTimeOriginal,
|
||||
timeZone = dto.timeZone,
|
||||
make = dto.make,
|
||||
model = dto.model,
|
||||
lens = dto.lensModel,
|
||||
f = dto.fNumber?.toDouble(),
|
||||
mm = dto.focalLength?.toDouble(),
|
||||
iso = dto.iso?.toInt(),
|
||||
exposureSeconds = _exposureTimeToSeconds(dto.exposureTime),
|
||||
lat = dto.latitude?.toDouble(),
|
||||
long = dto.longitude?.toDouble(),
|
||||
city = dto.city,
|
||||
state = dto.state,
|
||||
country = dto.country,
|
||||
description = dto.description,
|
||||
orientation = dto.orientation;
|
||||
|
||||
ExifInfo({
|
||||
this.id,
|
||||
this.fileSize,
|
||||
this.dateTimeOriginal,
|
||||
this.timeZone,
|
||||
this.make,
|
||||
this.model,
|
||||
this.lens,
|
||||
this.f,
|
||||
this.mm,
|
||||
this.iso,
|
||||
this.exposureSeconds,
|
||||
this.lat,
|
||||
this.long,
|
||||
this.city,
|
||||
this.state,
|
||||
this.country,
|
||||
this.description,
|
||||
this.orientation,
|
||||
});
|
||||
|
||||
ExifInfo copyWith({
|
||||
Id? id,
|
||||
int? fileSize,
|
||||
DateTime? dateTimeOriginal,
|
||||
String? timeZone,
|
||||
String? make,
|
||||
String? model,
|
||||
String? lens,
|
||||
float? f,
|
||||
float? mm,
|
||||
short? iso,
|
||||
float? exposureSeconds,
|
||||
float? lat,
|
||||
float? long,
|
||||
String? city,
|
||||
String? state,
|
||||
String? country,
|
||||
String? description,
|
||||
String? orientation,
|
||||
}) =>
|
||||
ExifInfo(
|
||||
id: id ?? this.id,
|
||||
fileSize: fileSize ?? this.fileSize,
|
||||
dateTimeOriginal: dateTimeOriginal ?? this.dateTimeOriginal,
|
||||
timeZone: timeZone ?? this.timeZone,
|
||||
make: make ?? this.make,
|
||||
model: model ?? this.model,
|
||||
lens: lens ?? this.lens,
|
||||
f: f ?? this.f,
|
||||
mm: mm ?? this.mm,
|
||||
iso: iso ?? this.iso,
|
||||
exposureSeconds: exposureSeconds ?? this.exposureSeconds,
|
||||
lat: lat ?? this.lat,
|
||||
long: long ?? this.long,
|
||||
city: city ?? this.city,
|
||||
state: state ?? this.state,
|
||||
country: country ?? this.country,
|
||||
description: description ?? this.description,
|
||||
orientation: orientation ?? this.orientation,
|
||||
);
|
||||
|
||||
@override
|
||||
bool operator ==(other) {
|
||||
if (other is! ExifInfo) return false;
|
||||
return id == other.id &&
|
||||
fileSize == other.fileSize &&
|
||||
dateTimeOriginal == other.dateTimeOriginal &&
|
||||
timeZone == other.timeZone &&
|
||||
make == other.make &&
|
||||
model == other.model &&
|
||||
lens == other.lens &&
|
||||
f == other.f &&
|
||||
mm == other.mm &&
|
||||
iso == other.iso &&
|
||||
exposureSeconds == other.exposureSeconds &&
|
||||
lat == other.lat &&
|
||||
long == other.long &&
|
||||
city == other.city &&
|
||||
state == other.state &&
|
||||
country == other.country &&
|
||||
description == other.description &&
|
||||
orientation == other.orientation;
|
||||
}
|
||||
|
||||
@override
|
||||
@ignore
|
||||
int get hashCode =>
|
||||
id.hashCode ^
|
||||
fileSize.hashCode ^
|
||||
dateTimeOriginal.hashCode ^
|
||||
timeZone.hashCode ^
|
||||
make.hashCode ^
|
||||
model.hashCode ^
|
||||
lens.hashCode ^
|
||||
f.hashCode ^
|
||||
mm.hashCode ^
|
||||
iso.hashCode ^
|
||||
exposureSeconds.hashCode ^
|
||||
lat.hashCode ^
|
||||
long.hashCode ^
|
||||
city.hashCode ^
|
||||
state.hashCode ^
|
||||
country.hashCode ^
|
||||
description.hashCode ^
|
||||
orientation.hashCode;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return """
|
||||
{
|
||||
id: $id,
|
||||
fileSize: $fileSize,
|
||||
dateTimeOriginal: $dateTimeOriginal,
|
||||
timeZone: $timeZone,
|
||||
make: $make,
|
||||
model: $model,
|
||||
lens: $lens,
|
||||
f: $f,
|
||||
mm: $mm,
|
||||
iso: $iso,
|
||||
exposureSeconds: $exposureSeconds,
|
||||
lat: $lat,
|
||||
long: $long,
|
||||
city: $city,
|
||||
state: $state,
|
||||
country: $country,
|
||||
description: $description,
|
||||
orientation: $orientation
|
||||
}""";
|
||||
}
|
||||
}
|
||||
|
||||
bool _isOrientationFlipped(String? orientation) {
|
||||
final value = orientation != null ? int.tryParse(orientation) : null;
|
||||
if (value == null) {
|
||||
return false;
|
||||
}
|
||||
final isRotated90CW = value == 5 || value == 6 || value == 90;
|
||||
final isRotated270CW = value == 7 || value == 8 || value == -90;
|
||||
return isRotated90CW || isRotated270CW;
|
||||
}
|
||||
|
||||
double? _exposureTimeToSeconds(String? s) {
|
||||
if (s == null) {
|
||||
return null;
|
||||
}
|
||||
double? value = double.tryParse(s);
|
||||
if (value != null) {
|
||||
return value;
|
||||
}
|
||||
final parts = s.split("/");
|
||||
if (parts.length == 2) {
|
||||
final numerator = double.tryParse(parts[0]);
|
||||
final denominator = double.tryParse(parts[1]);
|
||||
if (numerator != null && denominator != null) {
|
||||
return numerator / denominator;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
3024
mobile/lib/entities/exif_info.entity.g.dart
generated
3024
mobile/lib/entities/exif_info.entity.g.dart
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user