mirror of
https://github.com/immich-app/immich.git
synced 2025-06-02 21:24:28 -04:00
refactor: exif entity (#16621)
Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
This commit is contained in:
parent
4ebc25c754
commit
fe931faf17
@ -67,7 +67,7 @@ custom_lint:
|
|||||||
- lib/entities/*.entity.dart
|
- lib/entities/*.entity.dart
|
||||||
- lib/repositories/{album,asset,backup,database,etag,exif_info,user,timeline,partner}.repository.dart
|
- lib/repositories/{album,asset,backup,database,etag,exif_info,user,timeline,partner}.repository.dart
|
||||||
- lib/infrastructure/entities/*.entity.dart
|
- lib/infrastructure/entities/*.entity.dart
|
||||||
- lib/infrastructure/repositories/{store,db,log}.repository.dart
|
- lib/infrastructure/repositories/{store,db,log,exif}.repository.dart
|
||||||
- lib/providers/infrastructure/db.provider.dart
|
- lib/providers/infrastructure/db.provider.dart
|
||||||
# acceptable exceptions for the time being (until Isar is fully replaced)
|
# acceptable exceptions for the time being (until Isar is fully replaced)
|
||||||
- lib/providers/app_life_cycle.provider.dart
|
- lib/providers/app_life_cycle.provider.dart
|
||||||
@ -90,6 +90,7 @@ custom_lint:
|
|||||||
# required / wanted
|
# required / wanted
|
||||||
- lib/repositories/*_api.repository.dart
|
- lib/repositories/*_api.repository.dart
|
||||||
- lib/infrastructure/repositories/*_api.repository.dart
|
- lib/infrastructure/repositories/*_api.repository.dart
|
||||||
|
- lib/infrastructure/utils/*.converter.dart
|
||||||
# acceptable exceptions for the time being
|
# acceptable exceptions for the time being
|
||||||
- lib/entities/{album,asset,exif_info,user}.entity.dart # to convert DTOs to entities
|
- lib/entities/{album,asset,exif_info,user}.entity.dart # to convert DTOs to entities
|
||||||
- lib/utils/{image_url_builder,openapi_patching}.dart # utils are fine
|
- lib/utils/{image_url_builder,openapi_patching}.dart # utils are fine
|
||||||
|
14
mobile/lib/domain/interfaces/exif.interface.dart
Normal file
14
mobile/lib/domain/interfaces/exif.interface.dart
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import 'package:immich_mobile/domain/interfaces/db.interface.dart';
|
||||||
|
import 'package:immich_mobile/domain/models/exif.model.dart';
|
||||||
|
|
||||||
|
abstract interface class IExifInfoRepository implements IDatabaseRepository {
|
||||||
|
Future<ExifInfo?> get(int assetId);
|
||||||
|
|
||||||
|
Future<ExifInfo> update(ExifInfo exifInfo);
|
||||||
|
|
||||||
|
Future<List<ExifInfo>> updateAll(List<ExifInfo> exifInfos);
|
||||||
|
|
||||||
|
Future<void> delete(int assetId);
|
||||||
|
|
||||||
|
Future<void> deleteAll();
|
||||||
|
}
|
@ -1,8 +1,9 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:immich_mobile/domain/interfaces/db.interface.dart';
|
||||||
import 'package:immich_mobile/domain/models/log.model.dart';
|
import 'package:immich_mobile/domain/models/log.model.dart';
|
||||||
|
|
||||||
abstract interface class ILogRepository {
|
abstract interface class ILogRepository implements IDatabaseRepository {
|
||||||
Future<bool> insert(LogMessage log);
|
Future<bool> insert(LogMessage log);
|
||||||
|
|
||||||
Future<bool> insertAll(Iterable<LogMessage> logs);
|
Future<bool> insertAll(Iterable<LogMessage> logs);
|
||||||
|
177
mobile/lib/domain/models/exif.model.dart
Normal file
177
mobile/lib/domain/models/exif.model.dart
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
class ExifInfo {
|
||||||
|
final int? assetId;
|
||||||
|
final int? fileSize;
|
||||||
|
final String? description;
|
||||||
|
final bool isFlipped;
|
||||||
|
final String? orientation;
|
||||||
|
final String? timeZone;
|
||||||
|
final DateTime? dateTimeOriginal;
|
||||||
|
|
||||||
|
// GPS
|
||||||
|
final double? latitude;
|
||||||
|
final double? longitude;
|
||||||
|
final String? city;
|
||||||
|
final String? state;
|
||||||
|
final String? country;
|
||||||
|
|
||||||
|
// Camera related
|
||||||
|
final String? make;
|
||||||
|
final String? model;
|
||||||
|
final String? lens;
|
||||||
|
final double? f;
|
||||||
|
final double? mm;
|
||||||
|
final int? iso;
|
||||||
|
final double? exposureSeconds;
|
||||||
|
|
||||||
|
bool get hasCoordinates =>
|
||||||
|
latitude != null && longitude != null && latitude != 0 && longitude != 0;
|
||||||
|
|
||||||
|
String get exposureTime {
|
||||||
|
if (exposureSeconds == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
if (exposureSeconds! < 1) {
|
||||||
|
return "1/${(1.0 / exposureSeconds!).round()} s";
|
||||||
|
}
|
||||||
|
return "${exposureSeconds!.toStringAsFixed(1)} s";
|
||||||
|
}
|
||||||
|
|
||||||
|
String get fNumber => f == null ? "" : f!.toStringAsFixed(1);
|
||||||
|
|
||||||
|
String get focalLength => mm == null ? "" : mm!.toStringAsFixed(1);
|
||||||
|
|
||||||
|
const ExifInfo({
|
||||||
|
this.assetId,
|
||||||
|
this.fileSize,
|
||||||
|
this.description,
|
||||||
|
this.orientation,
|
||||||
|
this.timeZone,
|
||||||
|
this.dateTimeOriginal,
|
||||||
|
this.isFlipped = false,
|
||||||
|
this.latitude,
|
||||||
|
this.longitude,
|
||||||
|
this.city,
|
||||||
|
this.state,
|
||||||
|
this.country,
|
||||||
|
this.make,
|
||||||
|
this.model,
|
||||||
|
this.lens,
|
||||||
|
this.f,
|
||||||
|
this.mm,
|
||||||
|
this.iso,
|
||||||
|
this.exposureSeconds,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(covariant ExifInfo other) {
|
||||||
|
if (identical(this, other)) return true;
|
||||||
|
|
||||||
|
return other.fileSize == fileSize &&
|
||||||
|
other.description == description &&
|
||||||
|
other.orientation == orientation &&
|
||||||
|
other.timeZone == timeZone &&
|
||||||
|
other.dateTimeOriginal == dateTimeOriginal &&
|
||||||
|
other.latitude == latitude &&
|
||||||
|
other.longitude == longitude &&
|
||||||
|
other.city == city &&
|
||||||
|
other.state == state &&
|
||||||
|
other.country == country &&
|
||||||
|
other.make == make &&
|
||||||
|
other.model == model &&
|
||||||
|
other.lens == lens &&
|
||||||
|
other.f == f &&
|
||||||
|
other.mm == mm &&
|
||||||
|
other.iso == iso &&
|
||||||
|
other.exposureSeconds == exposureSeconds &&
|
||||||
|
other.assetId == assetId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode {
|
||||||
|
return fileSize.hashCode ^
|
||||||
|
description.hashCode ^
|
||||||
|
orientation.hashCode ^
|
||||||
|
timeZone.hashCode ^
|
||||||
|
dateTimeOriginal.hashCode ^
|
||||||
|
latitude.hashCode ^
|
||||||
|
longitude.hashCode ^
|
||||||
|
city.hashCode ^
|
||||||
|
state.hashCode ^
|
||||||
|
country.hashCode ^
|
||||||
|
make.hashCode ^
|
||||||
|
model.hashCode ^
|
||||||
|
lens.hashCode ^
|
||||||
|
f.hashCode ^
|
||||||
|
mm.hashCode ^
|
||||||
|
iso.hashCode ^
|
||||||
|
exposureSeconds.hashCode ^
|
||||||
|
assetId.hashCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return '''{
|
||||||
|
fileSize: ${fileSize ?? 'NA'},
|
||||||
|
description: ${description ?? 'NA'},
|
||||||
|
orientation: ${orientation ?? 'NA'},
|
||||||
|
timeZone: ${timeZone ?? 'NA'},
|
||||||
|
dateTimeOriginal: ${dateTimeOriginal ?? 'NA'},
|
||||||
|
latitude: ${latitude ?? 'NA'},
|
||||||
|
longitude: ${longitude ?? 'NA'},
|
||||||
|
city: ${city ?? 'NA'},
|
||||||
|
state: ${state ?? 'NA'},
|
||||||
|
country: ${country ?? '<NA>'},
|
||||||
|
make: ${make ?? 'NA'},
|
||||||
|
model: ${model ?? 'NA'},
|
||||||
|
lens: ${lens ?? 'NA'},
|
||||||
|
f: ${f ?? 'NA'},
|
||||||
|
mm: ${mm ?? '<NA>'},
|
||||||
|
iso: ${iso ?? 'NA'},
|
||||||
|
exposureSeconds: ${exposureSeconds ?? 'NA'},
|
||||||
|
}''';
|
||||||
|
}
|
||||||
|
|
||||||
|
ExifInfo copyWith({
|
||||||
|
int? assetId,
|
||||||
|
int? fileSize,
|
||||||
|
String? description,
|
||||||
|
String? orientation,
|
||||||
|
String? timeZone,
|
||||||
|
DateTime? dateTimeOriginal,
|
||||||
|
double? latitude,
|
||||||
|
double? longitude,
|
||||||
|
String? city,
|
||||||
|
String? state,
|
||||||
|
String? country,
|
||||||
|
bool? isFlipped,
|
||||||
|
String? make,
|
||||||
|
String? model,
|
||||||
|
String? lens,
|
||||||
|
double? f,
|
||||||
|
double? mm,
|
||||||
|
int? iso,
|
||||||
|
double? exposureSeconds,
|
||||||
|
}) {
|
||||||
|
return ExifInfo(
|
||||||
|
assetId: assetId ?? this.assetId,
|
||||||
|
fileSize: fileSize ?? this.fileSize,
|
||||||
|
description: description ?? this.description,
|
||||||
|
orientation: orientation ?? this.orientation,
|
||||||
|
timeZone: timeZone ?? this.timeZone,
|
||||||
|
dateTimeOriginal: dateTimeOriginal ?? this.dateTimeOriginal,
|
||||||
|
isFlipped: isFlipped ?? this.isFlipped,
|
||||||
|
latitude: latitude ?? this.latitude,
|
||||||
|
longitude: longitude ?? this.longitude,
|
||||||
|
city: city ?? this.city,
|
||||||
|
state: state ?? this.state,
|
||||||
|
country: country ?? this.country,
|
||||||
|
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,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,13 +1,16 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
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:immich_mobile/utils/hash.dart';
|
||||||
import 'package:isar/isar.dart';
|
import 'package:isar/isar.dart';
|
||||||
import 'package:openapi/api.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:path/path.dart' as p;
|
||||||
|
import 'package:photo_manager/photo_manager.dart' show AssetEntity;
|
||||||
|
|
||||||
part 'asset.entity.g.dart';
|
part 'asset.entity.g.dart';
|
||||||
|
|
||||||
@ -27,8 +30,9 @@ class Asset {
|
|||||||
width = remote.exifInfo?.exifImageWidth?.toInt(),
|
width = remote.exifInfo?.exifImageWidth?.toInt(),
|
||||||
livePhotoVideoId = remote.livePhotoVideoId,
|
livePhotoVideoId = remote.livePhotoVideoId,
|
||||||
ownerId = fastHash(remote.ownerId),
|
ownerId = fastHash(remote.ownerId),
|
||||||
exifInfo =
|
exifInfo = remote.exifInfo == null
|
||||||
remote.exifInfo != null ? ExifInfo.fromDto(remote.exifInfo!) : null,
|
? null
|
||||||
|
: ExifDtoConverter.fromDto(remote.exifInfo!),
|
||||||
isFavorite = remote.isFavorite,
|
isFavorite = remote.isFavorite,
|
||||||
isArchived = remote.isArchived,
|
isArchived = remote.isArchived,
|
||||||
isTrashed = remote.isTrashed,
|
isTrashed = remote.isTrashed,
|
||||||
@ -359,14 +363,14 @@ class Asset {
|
|||||||
localId: localId,
|
localId: localId,
|
||||||
width: a.width ?? width,
|
width: a.width ?? width,
|
||||||
height: a.height ?? height,
|
height: a.height ?? height,
|
||||||
exifInfo: a.exifInfo?.copyWith(id: id) ?? exifInfo,
|
exifInfo: a.exifInfo?.copyWith(assetId: id) ?? exifInfo,
|
||||||
);
|
);
|
||||||
} else if (isRemote) {
|
} else if (isRemote) {
|
||||||
return _copyWith(
|
return _copyWith(
|
||||||
localId: localId ?? a.localId,
|
localId: localId ?? a.localId,
|
||||||
width: width ?? a.width,
|
width: width ?? a.width,
|
||||||
height: height ?? a.height,
|
height: height ?? a.height,
|
||||||
exifInfo: exifInfo ?? a.exifInfo?.copyWith(id: id),
|
exifInfo: exifInfo ?? a.exifInfo?.copyWith(assetId: id),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
// TODO: Revisit this and remove all bool field assignments
|
// TODO: Revisit this and remove all bool field assignments
|
||||||
@ -407,7 +411,7 @@ class Asset {
|
|||||||
isArchived: a.isArchived,
|
isArchived: a.isArchived,
|
||||||
isTrashed: a.isTrashed,
|
isTrashed: a.isTrashed,
|
||||||
isOffline: a.isOffline,
|
isOffline: a.isOffline,
|
||||||
exifInfo: a.exifInfo?.copyWith(id: id) ?? exifInfo,
|
exifInfo: a.exifInfo?.copyWith(assetId: id) ?? exifInfo,
|
||||||
thumbhash: a.thumbhash,
|
thumbhash: a.thumbhash,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@ -416,7 +420,8 @@ class Asset {
|
|||||||
localId: localId ?? a.localId,
|
localId: localId ?? a.localId,
|
||||||
width: width ?? a.width,
|
width: width ?? a.width,
|
||||||
height: height ?? a.height,
|
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 {
|
Future<void> put(Isar db) async {
|
||||||
await db.assets.put(this);
|
await db.assets.put(this);
|
||||||
if (exifInfo != null) {
|
if (exifInfo != null) {
|
||||||
exifInfo!.id = id;
|
await db.exifInfos
|
||||||
await db.exifInfos.put(exifInfo!);
|
.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;
|
|
||||||
}
|
|
90
mobile/lib/infrastructure/entities/exif.entity.dart
Normal file
90
mobile/lib/infrastructure/entities/exif.entity.dart
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
import 'package:immich_mobile/domain/models/exif.model.dart' as domain;
|
||||||
|
import 'package:isar/isar.dart';
|
||||||
|
|
||||||
|
part 'exif.entity.g.dart';
|
||||||
|
|
||||||
|
/// Exif information 1:1 relation with Asset
|
||||||
|
@Collection(inheritance: false)
|
||||||
|
class ExifInfo {
|
||||||
|
final Id? id;
|
||||||
|
final int? fileSize;
|
||||||
|
final DateTime? dateTimeOriginal;
|
||||||
|
final String? timeZone;
|
||||||
|
final String? make;
|
||||||
|
final String? model;
|
||||||
|
final String? lens;
|
||||||
|
final float? f;
|
||||||
|
final float? mm;
|
||||||
|
final short? iso;
|
||||||
|
final float? exposureSeconds;
|
||||||
|
final float? lat;
|
||||||
|
final float? long;
|
||||||
|
final String? city;
|
||||||
|
final String? state;
|
||||||
|
final String? country;
|
||||||
|
final String? description;
|
||||||
|
final String? orientation;
|
||||||
|
|
||||||
|
const 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,
|
||||||
|
});
|
||||||
|
|
||||||
|
static ExifInfo fromDto(domain.ExifInfo dto) => ExifInfo(
|
||||||
|
id: dto.assetId,
|
||||||
|
fileSize: dto.fileSize,
|
||||||
|
dateTimeOriginal: dto.dateTimeOriginal,
|
||||||
|
timeZone: dto.timeZone,
|
||||||
|
make: dto.make,
|
||||||
|
model: dto.model,
|
||||||
|
lens: dto.lens,
|
||||||
|
f: dto.f,
|
||||||
|
mm: dto.mm,
|
||||||
|
iso: dto.iso?.toInt(),
|
||||||
|
exposureSeconds: dto.exposureSeconds,
|
||||||
|
lat: dto.latitude,
|
||||||
|
long: dto.longitude,
|
||||||
|
city: dto.city,
|
||||||
|
state: dto.state,
|
||||||
|
country: dto.country,
|
||||||
|
description: dto.description,
|
||||||
|
orientation: dto.orientation,
|
||||||
|
);
|
||||||
|
|
||||||
|
domain.ExifInfo toDto() => domain.ExifInfo(
|
||||||
|
assetId: id,
|
||||||
|
fileSize: fileSize,
|
||||||
|
description: description,
|
||||||
|
orientation: orientation,
|
||||||
|
timeZone: timeZone,
|
||||||
|
dateTimeOriginal: dateTimeOriginal,
|
||||||
|
latitude: lat,
|
||||||
|
longitude: long,
|
||||||
|
city: city,
|
||||||
|
state: state,
|
||||||
|
country: country,
|
||||||
|
make: make,
|
||||||
|
model: model,
|
||||||
|
lens: lens,
|
||||||
|
f: f,
|
||||||
|
mm: mm,
|
||||||
|
iso: iso?.toInt(),
|
||||||
|
exposureSeconds: exposureSeconds,
|
||||||
|
);
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
part of 'exif_info.entity.dart';
|
part of 'exif.entity.dart';
|
||||||
|
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
// IsarCollectionGenerator
|
// IsarCollectionGenerator
|
||||||
@ -288,9 +288,7 @@ List<IsarLinkBase<dynamic>> _exifInfoGetLinks(ExifInfo object) {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
void _exifInfoAttach(IsarCollection<dynamic> col, Id id, ExifInfo object) {
|
void _exifInfoAttach(IsarCollection<dynamic> col, Id id, ExifInfo object) {}
|
||||||
object.id = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
extension ExifInfoQueryWhereSort on QueryBuilder<ExifInfo, ExifInfo, QWhere> {
|
extension ExifInfoQueryWhereSort on QueryBuilder<ExifInfo, ExifInfo, QWhere> {
|
||||||
QueryBuilder<ExifInfo, ExifInfo, QAfterWhere> anyId() {
|
QueryBuilder<ExifInfo, ExifInfo, QAfterWhere> anyId() {
|
50
mobile/lib/infrastructure/repositories/exif.repository.dart
Normal file
50
mobile/lib/infrastructure/repositories/exif.repository.dart
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import 'package:immich_mobile/domain/interfaces/exif.interface.dart';
|
||||||
|
import 'package:immich_mobile/domain/models/exif.model.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/entities/exif.entity.dart'
|
||||||
|
as entity;
|
||||||
|
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
||||||
|
import 'package:isar/isar.dart';
|
||||||
|
|
||||||
|
class IsarExifRepository extends IsarDatabaseRepository
|
||||||
|
implements IExifInfoRepository {
|
||||||
|
final Isar _db;
|
||||||
|
|
||||||
|
const IsarExifRepository(this._db) : super(_db);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> delete(int assetId) async {
|
||||||
|
await transaction(() async {
|
||||||
|
await _db.exifInfos.delete(assetId);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> deleteAll() async {
|
||||||
|
await transaction(() async {
|
||||||
|
await _db.exifInfos.clear();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<ExifInfo?> get(int assetId) async {
|
||||||
|
return (await _db.exifInfos.get(assetId))?.toDto();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<ExifInfo> update(ExifInfo exifInfo) {
|
||||||
|
return transaction(() async {
|
||||||
|
await _db.exifInfos.put(entity.ExifInfo.fromDto(exifInfo));
|
||||||
|
return exifInfo;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<List<ExifInfo>> updateAll(List<ExifInfo> exifInfos) {
|
||||||
|
return transaction(() async {
|
||||||
|
await _db.exifInfos.putAll(
|
||||||
|
exifInfos.map(entity.ExifInfo.fromDto).toList(),
|
||||||
|
);
|
||||||
|
return exifInfos;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
56
mobile/lib/infrastructure/utils/exif.converter.dart
Normal file
56
mobile/lib/infrastructure/utils/exif.converter.dart
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import 'package:immich_mobile/domain/models/exif.model.dart';
|
||||||
|
import 'package:openapi/api.dart';
|
||||||
|
|
||||||
|
abstract final class ExifDtoConverter {
|
||||||
|
static ExifInfo fromDto(ExifResponseDto dto) {
|
||||||
|
return ExifInfo(
|
||||||
|
fileSize: dto.fileSizeInByte,
|
||||||
|
description: dto.description,
|
||||||
|
orientation: dto.orientation,
|
||||||
|
timeZone: dto.timeZone,
|
||||||
|
dateTimeOriginal: dto.dateTimeOriginal,
|
||||||
|
isFlipped: _isOrientationFlipped(dto.orientation),
|
||||||
|
latitude: dto.latitude?.toDouble(),
|
||||||
|
longitude: dto.longitude?.toDouble(),
|
||||||
|
city: dto.city,
|
||||||
|
state: dto.state,
|
||||||
|
country: dto.country,
|
||||||
|
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),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool _isOrientationFlipped(String? orientation) {
|
||||||
|
final value = orientation == null ? null : int.tryParse(orientation);
|
||||||
|
if (value == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final isRotated90CW = value == 5 || value == 6 || value == 90;
|
||||||
|
final isRotated270CW = value == 7 || value == 8 || value == -90;
|
||||||
|
return isRotated90CW || isRotated270CW;
|
||||||
|
}
|
||||||
|
|
||||||
|
static 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.firstOrNull ?? "-");
|
||||||
|
final denominator = double.tryParse(parts.lastOrNull ?? "-");
|
||||||
|
if (numerator != null && denominator != null) {
|
||||||
|
return numerator / denominator;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@ -1,14 +0,0 @@
|
|||||||
import 'package:immich_mobile/entities/exif_info.entity.dart';
|
|
||||||
import 'package:immich_mobile/interfaces/database.interface.dart';
|
|
||||||
|
|
||||||
abstract interface class IExifInfoRepository implements IDatabaseRepository {
|
|
||||||
Future<ExifInfo?> get(int id);
|
|
||||||
|
|
||||||
Future<ExifInfo> update(ExifInfo exifInfo);
|
|
||||||
|
|
||||||
Future<List<ExifInfo>> updateAll(List<ExifInfo> exifInfos);
|
|
||||||
|
|
||||||
Future<void> delete(int id);
|
|
||||||
|
|
||||||
Future<void> clearTable();
|
|
||||||
}
|
|
10
mobile/lib/providers/infrastructure/exif.provider.dart
Normal file
10
mobile/lib/providers/infrastructure/exif.provider.dart
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import 'package:immich_mobile/domain/interfaces/exif.interface.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/repositories/exif.repository.dart';
|
||||||
|
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
|
||||||
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
|
|
||||||
|
part 'exif.provider.g.dart';
|
||||||
|
|
||||||
|
@Riverpod(keepAlive: true)
|
||||||
|
IExifInfoRepository exifRepository(ExifRepositoryRef ref) =>
|
||||||
|
IsarExifRepository(ref.watch(isarProvider));
|
25
mobile/lib/providers/infrastructure/exif.provider.g.dart
generated
Normal file
25
mobile/lib/providers/infrastructure/exif.provider.g.dart
generated
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'exif.provider.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// RiverpodGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
String _$exifRepositoryHash() => r'f8f94d2a43fa79b08e58d60e81d7877825f33ec0';
|
||||||
|
|
||||||
|
/// See also [exifRepository].
|
||||||
|
@ProviderFor(exifRepository)
|
||||||
|
final exifRepositoryProvider = Provider<IExifInfoRepository>.internal(
|
||||||
|
exifRepository,
|
||||||
|
name: r'exifRepositoryProvider',
|
||||||
|
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||||
|
? null
|
||||||
|
: _$exifRepositoryHash,
|
||||||
|
dependencies: null,
|
||||||
|
allTransitiveDependencies: null,
|
||||||
|
);
|
||||||
|
|
||||||
|
typedef ExifRepositoryRef = ProviderRef<IExifInfoRepository>;
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member
|
@ -6,8 +6,8 @@ import 'package:immich_mobile/entities/android_device_asset.entity.dart';
|
|||||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||||
import 'package:immich_mobile/entities/device_asset.entity.dart';
|
import 'package:immich_mobile/entities/device_asset.entity.dart';
|
||||||
import 'package:immich_mobile/entities/duplicated_asset.entity.dart';
|
import 'package:immich_mobile/entities/duplicated_asset.entity.dart';
|
||||||
import 'package:immich_mobile/entities/exif_info.entity.dart';
|
|
||||||
import 'package:immich_mobile/entities/ios_device_asset.entity.dart';
|
import 'package:immich_mobile/entities/ios_device_asset.entity.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/entities/exif.entity.dart';
|
||||||
import 'package:immich_mobile/interfaces/asset.interface.dart';
|
import 'package:immich_mobile/interfaces/asset.interface.dart';
|
||||||
import 'package:immich_mobile/providers/db.provider.dart';
|
import 'package:immich_mobile/providers/db.provider.dart';
|
||||||
import 'package:immich_mobile/repositories/database.repository.dart';
|
import 'package:immich_mobile/repositories/database.repository.dart';
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/domain/models/exif.model.dart';
|
||||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
import 'package:immich_mobile/domain/models/store.model.dart';
|
||||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||||
import 'package:immich_mobile/entities/exif_info.entity.dart';
|
|
||||||
import 'package:immich_mobile/entities/store.entity.dart';
|
import 'package:immich_mobile/entities/store.entity.dart';
|
||||||
import 'package:immich_mobile/interfaces/asset_media.interface.dart';
|
import 'package:immich_mobile/interfaces/asset_media.interface.dart';
|
||||||
import 'package:photo_manager/photo_manager.dart' hide AssetType;
|
import 'package:photo_manager/photo_manager.dart' hide AssetType;
|
||||||
@ -39,7 +39,8 @@ class AssetMediaRepository implements IAssetMediaRepository {
|
|||||||
asset.fileCreatedAt = asset.fileModifiedAt;
|
asset.fileCreatedAt = asset.fileModifiedAt;
|
||||||
}
|
}
|
||||||
if (local.latitude != null) {
|
if (local.latitude != null) {
|
||||||
asset.exifInfo = ExifInfo(lat: local.latitude, long: local.longitude);
|
asset.exifInfo =
|
||||||
|
ExifInfo(latitude: local.latitude, longitude: local.longitude);
|
||||||
}
|
}
|
||||||
asset.local = local;
|
asset.local = local;
|
||||||
return asset;
|
return asset;
|
||||||
|
@ -5,9 +5,9 @@ import 'package:immich_mobile/domain/models/store.model.dart';
|
|||||||
import 'package:immich_mobile/entities/album.entity.dart';
|
import 'package:immich_mobile/entities/album.entity.dart';
|
||||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||||
import 'package:immich_mobile/entities/etag.entity.dart';
|
import 'package:immich_mobile/entities/etag.entity.dart';
|
||||||
import 'package:immich_mobile/entities/exif_info.entity.dart';
|
|
||||||
import 'package:immich_mobile/entities/store.entity.dart';
|
import 'package:immich_mobile/entities/store.entity.dart';
|
||||||
import 'package:immich_mobile/entities/user.entity.dart';
|
import 'package:immich_mobile/entities/user.entity.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/entities/exif.entity.dart';
|
||||||
import 'package:immich_mobile/interfaces/auth.interface.dart';
|
import 'package:immich_mobile/interfaces/auth.interface.dart';
|
||||||
import 'package:immich_mobile/models/auth/auxilary_endpoint.model.dart';
|
import 'package:immich_mobile/models/auth/auxilary_endpoint.model.dart';
|
||||||
import 'package:immich_mobile/providers/db.provider.dart';
|
import 'package:immich_mobile/providers/db.provider.dart';
|
||||||
|
@ -1,36 +0,0 @@
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|
||||||
import 'package:immich_mobile/entities/exif_info.entity.dart';
|
|
||||||
import 'package:immich_mobile/interfaces/exif_info.interface.dart';
|
|
||||||
import 'package:immich_mobile/providers/db.provider.dart';
|
|
||||||
import 'package:immich_mobile/repositories/database.repository.dart';
|
|
||||||
|
|
||||||
final exifInfoRepositoryProvider =
|
|
||||||
Provider((ref) => ExifInfoRepository(ref.watch(dbProvider)));
|
|
||||||
|
|
||||||
class ExifInfoRepository extends DatabaseRepository
|
|
||||||
implements IExifInfoRepository {
|
|
||||||
ExifInfoRepository(super.db);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> delete(int id) => txn(() => db.exifInfos.delete(id));
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<ExifInfo?> get(int id) => db.exifInfos.get(id);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<ExifInfo> update(ExifInfo exifInfo) async {
|
|
||||||
await txn(() => db.exifInfos.put(exifInfo));
|
|
||||||
return exifInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<List<ExifInfo>> updateAll(List<ExifInfo> exifInfos) async {
|
|
||||||
await txn(() => db.exifInfos.putAll(exifInfos));
|
|
||||||
return exifInfos;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> clearTable() {
|
|
||||||
return txn(() => db.exifInfos.clear());
|
|
||||||
}
|
|
||||||
}
|
|
@ -4,6 +4,7 @@ import 'dart:io';
|
|||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/domain/interfaces/exif.interface.dart';
|
||||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||||
import 'package:immich_mobile/entities/backup_album.entity.dart';
|
import 'package:immich_mobile/entities/backup_album.entity.dart';
|
||||||
import 'package:immich_mobile/entities/user.entity.dart';
|
import 'package:immich_mobile/entities/user.entity.dart';
|
||||||
@ -12,16 +13,15 @@ import 'package:immich_mobile/interfaces/asset_api.interface.dart';
|
|||||||
import 'package:immich_mobile/interfaces/asset_media.interface.dart';
|
import 'package:immich_mobile/interfaces/asset_media.interface.dart';
|
||||||
import 'package:immich_mobile/interfaces/backup_album.interface.dart';
|
import 'package:immich_mobile/interfaces/backup_album.interface.dart';
|
||||||
import 'package:immich_mobile/interfaces/etag.interface.dart';
|
import 'package:immich_mobile/interfaces/etag.interface.dart';
|
||||||
import 'package:immich_mobile/interfaces/exif_info.interface.dart';
|
|
||||||
import 'package:immich_mobile/interfaces/user.interface.dart';
|
import 'package:immich_mobile/interfaces/user.interface.dart';
|
||||||
import 'package:immich_mobile/models/backup/backup_candidate.model.dart';
|
import 'package:immich_mobile/models/backup/backup_candidate.model.dart';
|
||||||
import 'package:immich_mobile/providers/api.provider.dart';
|
import 'package:immich_mobile/providers/api.provider.dart';
|
||||||
|
import 'package:immich_mobile/providers/infrastructure/exif.provider.dart';
|
||||||
import 'package:immich_mobile/repositories/asset.repository.dart';
|
import 'package:immich_mobile/repositories/asset.repository.dart';
|
||||||
import 'package:immich_mobile/repositories/asset_api.repository.dart';
|
import 'package:immich_mobile/repositories/asset_api.repository.dart';
|
||||||
import 'package:immich_mobile/repositories/asset_media.repository.dart';
|
import 'package:immich_mobile/repositories/asset_media.repository.dart';
|
||||||
import 'package:immich_mobile/repositories/backup.repository.dart';
|
import 'package:immich_mobile/repositories/backup.repository.dart';
|
||||||
import 'package:immich_mobile/repositories/etag.repository.dart';
|
import 'package:immich_mobile/repositories/etag.repository.dart';
|
||||||
import 'package:immich_mobile/repositories/exif_info.repository.dart';
|
|
||||||
import 'package:immich_mobile/repositories/user.repository.dart';
|
import 'package:immich_mobile/repositories/user.repository.dart';
|
||||||
import 'package:immich_mobile/services/album.service.dart';
|
import 'package:immich_mobile/services/album.service.dart';
|
||||||
import 'package:immich_mobile/services/api.service.dart';
|
import 'package:immich_mobile/services/api.service.dart';
|
||||||
@ -36,7 +36,7 @@ final assetServiceProvider = Provider(
|
|||||||
(ref) => AssetService(
|
(ref) => AssetService(
|
||||||
ref.watch(assetApiRepositoryProvider),
|
ref.watch(assetApiRepositoryProvider),
|
||||||
ref.watch(assetRepositoryProvider),
|
ref.watch(assetRepositoryProvider),
|
||||||
ref.watch(exifInfoRepositoryProvider),
|
ref.watch(exifRepositoryProvider),
|
||||||
ref.watch(userRepositoryProvider),
|
ref.watch(userRepositoryProvider),
|
||||||
ref.watch(etagRepositoryProvider),
|
ref.watch(etagRepositoryProvider),
|
||||||
ref.watch(backupAlbumRepositoryProvider),
|
ref.watch(backupAlbumRepositoryProvider),
|
||||||
@ -166,17 +166,18 @@ class AssetService {
|
|||||||
/// Loads the exif information from the database. If there is none, loads
|
/// Loads the exif information from the database. If there is none, loads
|
||||||
/// the exif info from the server (remote assets only)
|
/// the exif info from the server (remote assets only)
|
||||||
Future<Asset> loadExif(Asset a) async {
|
Future<Asset> loadExif(Asset a) async {
|
||||||
a.exifInfo ??= await _exifInfoRepository.get(a.id);
|
a.exifInfo ??= (await _exifInfoRepository.get(a.id));
|
||||||
// fileSize is always filled on the server but not set on client
|
// fileSize is always filled on the server but not set on client
|
||||||
if (a.exifInfo?.fileSize == null) {
|
if (a.exifInfo?.fileSize == null) {
|
||||||
if (a.isRemote) {
|
if (a.isRemote) {
|
||||||
final dto = await _apiService.assetsApi.getAssetInfo(a.remoteId!);
|
final dto = await _apiService.assetsApi.getAssetInfo(a.remoteId!);
|
||||||
if (dto != null && dto.exifInfo != null) {
|
if (dto != null && dto.exifInfo != null) {
|
||||||
final newExif = Asset.remote(dto).exifInfo!.copyWith(id: a.id);
|
final newExif = Asset.remote(dto).exifInfo!.copyWith(assetId: a.id);
|
||||||
a.exifInfo = newExif;
|
a.exifInfo = newExif;
|
||||||
if (newExif != a.exifInfo) {
|
if (newExif != a.exifInfo) {
|
||||||
if (a.isInDb) {
|
if (a.isInDb) {
|
||||||
_assetRepository.transaction(() => _assetRepository.update(a));
|
await _assetRepository
|
||||||
|
.transaction(() => _assetRepository.update(a));
|
||||||
} else {
|
} else {
|
||||||
debugPrint("[loadExif] parameter Asset is not from DB!");
|
debugPrint("[loadExif] parameter Asset is not from DB!");
|
||||||
}
|
}
|
||||||
@ -257,7 +258,8 @@ class AssetService {
|
|||||||
|
|
||||||
for (var element in assets) {
|
for (var element in assets) {
|
||||||
element.fileCreatedAt = DateTime.parse(updatedDt);
|
element.fileCreatedAt = DateTime.parse(updatedDt);
|
||||||
element.exifInfo?.dateTimeOriginal = DateTime.parse(updatedDt);
|
element.exifInfo ??= element.exifInfo
|
||||||
|
?.copyWith(dateTimeOriginal: DateTime.parse(updatedDt));
|
||||||
}
|
}
|
||||||
|
|
||||||
await _syncService.upsertAssetsWithExif(assets);
|
await _syncService.upsertAssetsWithExif(assets);
|
||||||
@ -283,8 +285,10 @@ class AssetService {
|
|||||||
);
|
);
|
||||||
|
|
||||||
for (var element in assets) {
|
for (var element in assets) {
|
||||||
element.exifInfo?.lat = location.latitude;
|
element.exifInfo ??= element.exifInfo?.copyWith(
|
||||||
element.exifInfo?.long = location.longitude;
|
latitude: location.latitude,
|
||||||
|
longitude: location.longitude,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
await _syncService.upsertAssetsWithExif(assets);
|
await _syncService.upsertAssetsWithExif(assets);
|
||||||
@ -348,7 +352,7 @@ class AssetService {
|
|||||||
String newDescription,
|
String newDescription,
|
||||||
) async {
|
) async {
|
||||||
final remoteAssetId = asset.remoteId;
|
final remoteAssetId = asset.remoteId;
|
||||||
final localExifId = asset.exifInfo?.id;
|
final localExifId = asset.exifInfo?.assetId;
|
||||||
|
|
||||||
// Guard [remoteAssetId] and [localExifId] null
|
// Guard [remoteAssetId] and [localExifId] null
|
||||||
if (remoteAssetId == null || localExifId == null) {
|
if (remoteAssetId == null || localExifId == null) {
|
||||||
@ -366,14 +370,14 @@ class AssetService {
|
|||||||
var exifInfo = await _exifInfoRepository.get(localExifId);
|
var exifInfo = await _exifInfoRepository.get(localExifId);
|
||||||
|
|
||||||
if (exifInfo != null) {
|
if (exifInfo != null) {
|
||||||
exifInfo.description = description;
|
await _exifInfoRepository
|
||||||
await _exifInfoRepository.update(exifInfo);
|
.update(exifInfo.copyWith(description: description));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String> getDescription(Asset asset) async {
|
Future<String> getDescription(Asset asset) async {
|
||||||
final localExifId = asset.exifInfo?.id;
|
final localExifId = asset.exifInfo?.assetId;
|
||||||
|
|
||||||
// Guard [remoteAssetId] and [localExifId] null
|
// Guard [remoteAssetId] and [localExifId] null
|
||||||
if (localExifId == null) {
|
if (localExifId == null) {
|
||||||
|
@ -11,9 +11,11 @@ import 'package:flutter/foundation.dart';
|
|||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/domain/interfaces/exif.interface.dart';
|
||||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
import 'package:immich_mobile/domain/models/store.model.dart';
|
||||||
import 'package:immich_mobile/entities/backup_album.entity.dart';
|
import 'package:immich_mobile/entities/backup_album.entity.dart';
|
||||||
import 'package:immich_mobile/entities/store.entity.dart';
|
import 'package:immich_mobile/entities/store.entity.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/repositories/exif.repository.dart';
|
||||||
import 'package:immich_mobile/interfaces/backup_album.interface.dart';
|
import 'package:immich_mobile/interfaces/backup_album.interface.dart';
|
||||||
import 'package:immich_mobile/models/backup/backup_candidate.model.dart';
|
import 'package:immich_mobile/models/backup/backup_candidate.model.dart';
|
||||||
import 'package:immich_mobile/models/backup/current_upload_asset.model.dart';
|
import 'package:immich_mobile/models/backup/current_upload_asset.model.dart';
|
||||||
@ -28,7 +30,6 @@ import 'package:immich_mobile/repositories/auth.repository.dart';
|
|||||||
import 'package:immich_mobile/repositories/auth_api.repository.dart';
|
import 'package:immich_mobile/repositories/auth_api.repository.dart';
|
||||||
import 'package:immich_mobile/repositories/backup.repository.dart';
|
import 'package:immich_mobile/repositories/backup.repository.dart';
|
||||||
import 'package:immich_mobile/repositories/etag.repository.dart';
|
import 'package:immich_mobile/repositories/etag.repository.dart';
|
||||||
import 'package:immich_mobile/repositories/exif_info.repository.dart';
|
|
||||||
import 'package:immich_mobile/repositories/file_media.repository.dart';
|
import 'package:immich_mobile/repositories/file_media.repository.dart';
|
||||||
import 'package:immich_mobile/repositories/network.repository.dart';
|
import 'package:immich_mobile/repositories/network.repository.dart';
|
||||||
import 'package:immich_mobile/repositories/partner_api.repository.dart';
|
import 'package:immich_mobile/repositories/partner_api.repository.dart';
|
||||||
@ -379,7 +380,7 @@ class BackgroundService {
|
|||||||
AlbumRepository albumRepository = AlbumRepository(db);
|
AlbumRepository albumRepository = AlbumRepository(db);
|
||||||
AssetRepository assetRepository = AssetRepository(db);
|
AssetRepository assetRepository = AssetRepository(db);
|
||||||
BackupAlbumRepository backupRepository = BackupAlbumRepository(db);
|
BackupAlbumRepository backupRepository = BackupAlbumRepository(db);
|
||||||
ExifInfoRepository exifInfoRepository = ExifInfoRepository(db);
|
IExifInfoRepository exifInfoRepository = IsarExifRepository(db);
|
||||||
ETagRepository eTagRepository = ETagRepository(db);
|
ETagRepository eTagRepository = ETagRepository(db);
|
||||||
AlbumMediaRepository albumMediaRepository = AlbumMediaRepository();
|
AlbumMediaRepository albumMediaRepository = AlbumMediaRepository();
|
||||||
FileMediaRepository fileMediaRepository = FileMediaRepository();
|
FileMediaRepository fileMediaRepository = FileMediaRepository();
|
||||||
|
@ -5,15 +5,16 @@ import 'package:collection/collection.dart';
|
|||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/domain/interfaces/exif.interface.dart';
|
||||||
|
import 'package:immich_mobile/domain/models/exif.model.dart';
|
||||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
import 'package:immich_mobile/domain/models/store.model.dart';
|
||||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||||
import 'package:immich_mobile/entities/exif_info.entity.dart';
|
|
||||||
import 'package:immich_mobile/entities/store.entity.dart';
|
import 'package:immich_mobile/entities/store.entity.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/utils/exif.converter.dart';
|
||||||
import 'package:immich_mobile/interfaces/asset.interface.dart';
|
import 'package:immich_mobile/interfaces/asset.interface.dart';
|
||||||
import 'package:immich_mobile/interfaces/exif_info.interface.dart';
|
|
||||||
import 'package:immich_mobile/interfaces/file_media.interface.dart';
|
import 'package:immich_mobile/interfaces/file_media.interface.dart';
|
||||||
|
import 'package:immich_mobile/providers/infrastructure/exif.provider.dart';
|
||||||
import 'package:immich_mobile/repositories/asset.repository.dart';
|
import 'package:immich_mobile/repositories/asset.repository.dart';
|
||||||
import 'package:immich_mobile/repositories/exif_info.repository.dart';
|
|
||||||
import 'package:immich_mobile/repositories/file_media.repository.dart';
|
import 'package:immich_mobile/repositories/file_media.repository.dart';
|
||||||
import 'package:immich_mobile/services/api.service.dart';
|
import 'package:immich_mobile/services/api.service.dart';
|
||||||
import 'package:immich_mobile/utils/bootstrap.dart';
|
import 'package:immich_mobile/utils/bootstrap.dart';
|
||||||
@ -149,11 +150,11 @@ class BackupVerificationService {
|
|||||||
) async {
|
) async {
|
||||||
if (remote.checksum == local.checksum) return false;
|
if (remote.checksum == local.checksum) return false;
|
||||||
ExifInfo? exif = remote.exifInfo;
|
ExifInfo? exif = remote.exifInfo;
|
||||||
if (exif != null && exif.lat != null) return false;
|
if (exif != null && exif.latitude != null) return false;
|
||||||
if (exif == null || exif.fileSize == null) {
|
if (exif == null || exif.fileSize == null) {
|
||||||
final dto = await apiService.assetsApi.getAssetInfo(remote.remoteId!);
|
final dto = await apiService.assetsApi.getAssetInfo(remote.remoteId!);
|
||||||
if (dto != null && dto.exifInfo != null) {
|
if (dto != null && dto.exifInfo != null) {
|
||||||
exif = ExifInfo.fromDto(dto.exifInfo!);
|
exif = ExifDtoConverter.fromDto(dto.exifInfo!);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
final file = await local.local!.originFile;
|
final file = await local.local!.originFile;
|
||||||
@ -162,7 +163,7 @@ class BackupVerificationService {
|
|||||||
if (exif.fileSize! == origSize || exif.fileSize! != origSize) {
|
if (exif.fileSize! == origSize || exif.fileSize! != origSize) {
|
||||||
final latLng = await local.local!.latlngAsync();
|
final latLng = await local.local!.latlngAsync();
|
||||||
|
|
||||||
if (exif.lat == null &&
|
if (exif.latitude == null &&
|
||||||
latLng.latitude != null &&
|
latLng.latitude != null &&
|
||||||
(remote.fileCreatedAt.isAtSameMomentAs(local.fileCreatedAt) ||
|
(remote.fileCreatedAt.isAtSameMomentAs(local.fileCreatedAt) ||
|
||||||
remote.fileModifiedAt.isAtSameMomentAs(local.fileModifiedAt) ||
|
remote.fileModifiedAt.isAtSameMomentAs(local.fileModifiedAt) ||
|
||||||
@ -215,6 +216,6 @@ final backupVerificationServiceProvider = Provider(
|
|||||||
(ref) => BackupVerificationService(
|
(ref) => BackupVerificationService(
|
||||||
ref.watch(fileMediaRepositoryProvider),
|
ref.watch(fileMediaRepositoryProvider),
|
||||||
ref.watch(assetRepositoryProvider),
|
ref.watch(assetRepositoryProvider),
|
||||||
ref.watch(exifInfoRepositoryProvider),
|
ref.watch(exifRepositoryProvider),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/interfaces/exif_info.interface.dart';
|
import 'package:immich_mobile/domain/interfaces/exif.interface.dart';
|
||||||
import 'package:immich_mobile/repositories/exif_info.repository.dart';
|
import 'package:immich_mobile/providers/infrastructure/exif.provider.dart';
|
||||||
|
|
||||||
final exifServiceProvider =
|
final exifServiceProvider =
|
||||||
Provider((ref) => ExifService(ref.watch(exifInfoRepositoryProvider)));
|
Provider((ref) => ExifService(ref.watch(exifRepositoryProvider)));
|
||||||
|
|
||||||
class ExifService {
|
class ExifService {
|
||||||
final IExifInfoRepository _exifInfoRepository;
|
final IExifInfoRepository _exifInfoRepository;
|
||||||
|
|
||||||
ExifService(this._exifInfoRepository);
|
const ExifService(this._exifInfoRepository);
|
||||||
|
|
||||||
Future<void> clearTable() {
|
Future<void> clearTable() {
|
||||||
return _exifInfoRepository.clearTable();
|
return _exifInfoRepository.deleteAll();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ import 'dart:async';
|
|||||||
|
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/domain/interfaces/exif.interface.dart';
|
||||||
import 'package:immich_mobile/entities/album.entity.dart';
|
import 'package:immich_mobile/entities/album.entity.dart';
|
||||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||||
import 'package:immich_mobile/entities/etag.entity.dart';
|
import 'package:immich_mobile/entities/etag.entity.dart';
|
||||||
@ -12,14 +13,13 @@ import 'package:immich_mobile/interfaces/album_api.interface.dart';
|
|||||||
import 'package:immich_mobile/interfaces/album_media.interface.dart';
|
import 'package:immich_mobile/interfaces/album_media.interface.dart';
|
||||||
import 'package:immich_mobile/interfaces/asset.interface.dart';
|
import 'package:immich_mobile/interfaces/asset.interface.dart';
|
||||||
import 'package:immich_mobile/interfaces/etag.interface.dart';
|
import 'package:immich_mobile/interfaces/etag.interface.dart';
|
||||||
import 'package:immich_mobile/interfaces/exif_info.interface.dart';
|
|
||||||
import 'package:immich_mobile/interfaces/user.interface.dart';
|
import 'package:immich_mobile/interfaces/user.interface.dart';
|
||||||
|
import 'package:immich_mobile/providers/infrastructure/exif.provider.dart';
|
||||||
import 'package:immich_mobile/repositories/album.repository.dart';
|
import 'package:immich_mobile/repositories/album.repository.dart';
|
||||||
import 'package:immich_mobile/repositories/album_api.repository.dart';
|
import 'package:immich_mobile/repositories/album_api.repository.dart';
|
||||||
import 'package:immich_mobile/repositories/album_media.repository.dart';
|
import 'package:immich_mobile/repositories/album_media.repository.dart';
|
||||||
import 'package:immich_mobile/repositories/asset.repository.dart';
|
import 'package:immich_mobile/repositories/asset.repository.dart';
|
||||||
import 'package:immich_mobile/repositories/etag.repository.dart';
|
import 'package:immich_mobile/repositories/etag.repository.dart';
|
||||||
import 'package:immich_mobile/repositories/exif_info.repository.dart';
|
|
||||||
import 'package:immich_mobile/repositories/user.repository.dart';
|
import 'package:immich_mobile/repositories/user.repository.dart';
|
||||||
import 'package:immich_mobile/services/entity.service.dart';
|
import 'package:immich_mobile/services/entity.service.dart';
|
||||||
import 'package:immich_mobile/services/hash.service.dart';
|
import 'package:immich_mobile/services/hash.service.dart';
|
||||||
@ -36,7 +36,7 @@ final syncServiceProvider = Provider(
|
|||||||
ref.watch(albumApiRepositoryProvider),
|
ref.watch(albumApiRepositoryProvider),
|
||||||
ref.watch(albumRepositoryProvider),
|
ref.watch(albumRepositoryProvider),
|
||||||
ref.watch(assetRepositoryProvider),
|
ref.watch(assetRepositoryProvider),
|
||||||
ref.watch(exifInfoRepositoryProvider),
|
ref.watch(exifRepositoryProvider),
|
||||||
ref.watch(userRepositoryProvider),
|
ref.watch(userRepositoryProvider),
|
||||||
ref.watch(etagRepositoryProvider),
|
ref.watch(etagRepositoryProvider),
|
||||||
),
|
),
|
||||||
@ -756,7 +756,7 @@ class SyncService {
|
|||||||
await _assetRepository.transaction(() async {
|
await _assetRepository.transaction(() async {
|
||||||
await _assetRepository.updateAll(assets);
|
await _assetRepository.updateAll(assets);
|
||||||
for (final Asset added in assets) {
|
for (final Asset added in assets) {
|
||||||
added.exifInfo?.id = added.id;
|
added.exifInfo ??= added.exifInfo?.copyWith(assetId: added.id);
|
||||||
}
|
}
|
||||||
await _exifInfoRepository.updateAll(exifInfos);
|
await _exifInfoRepository.updateAll(exifInfos);
|
||||||
});
|
});
|
||||||
|
@ -9,9 +9,9 @@ import 'package:immich_mobile/entities/asset.entity.dart';
|
|||||||
import 'package:immich_mobile/entities/backup_album.entity.dart';
|
import 'package:immich_mobile/entities/backup_album.entity.dart';
|
||||||
import 'package:immich_mobile/entities/duplicated_asset.entity.dart';
|
import 'package:immich_mobile/entities/duplicated_asset.entity.dart';
|
||||||
import 'package:immich_mobile/entities/etag.entity.dart';
|
import 'package:immich_mobile/entities/etag.entity.dart';
|
||||||
import 'package:immich_mobile/entities/exif_info.entity.dart';
|
|
||||||
import 'package:immich_mobile/entities/ios_device_asset.entity.dart';
|
import 'package:immich_mobile/entities/ios_device_asset.entity.dart';
|
||||||
import 'package:immich_mobile/entities/user.entity.dart';
|
import 'package:immich_mobile/entities/user.entity.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/entities/exif.entity.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/log.entity.dart';
|
import 'package:immich_mobile/infrastructure/entities/log.entity.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/store.entity.dart';
|
import 'package:immich_mobile/infrastructure/entities/store.entity.dart';
|
||||||
import 'package:immich_mobile/infrastructure/repositories/log.repository.dart';
|
import 'package:immich_mobile/infrastructure/repositories/log.repository.dart';
|
||||||
|
@ -4,9 +4,9 @@ import 'package:immich_mobile/domain/models/store.model.dart';
|
|||||||
import 'package:immich_mobile/entities/album.entity.dart';
|
import 'package:immich_mobile/entities/album.entity.dart';
|
||||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||||
import 'package:immich_mobile/entities/etag.entity.dart';
|
import 'package:immich_mobile/entities/etag.entity.dart';
|
||||||
import 'package:immich_mobile/entities/exif_info.entity.dart';
|
|
||||||
import 'package:immich_mobile/entities/store.entity.dart';
|
import 'package:immich_mobile/entities/store.entity.dart';
|
||||||
import 'package:immich_mobile/entities/user.entity.dart';
|
import 'package:immich_mobile/entities/user.entity.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/entities/exif.entity.dart';
|
||||||
import 'package:isar/isar.dart';
|
import 'package:isar/isar.dart';
|
||||||
|
|
||||||
const int targetVersion = 8;
|
const int targetVersion = 8;
|
||||||
|
@ -2,9 +2,9 @@ import 'package:easy_localization/easy_localization.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:fluttertoast/fluttertoast.dart';
|
import 'package:fluttertoast/fluttertoast.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||||
import 'package:immich_mobile/extensions/asset_extensions.dart';
|
import 'package:immich_mobile/extensions/asset_extensions.dart';
|
||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
|
||||||
import 'package:immich_mobile/providers/asset.provider.dart';
|
import 'package:immich_mobile/providers/asset.provider.dart';
|
||||||
import 'package:immich_mobile/services/asset.service.dart';
|
import 'package:immich_mobile/services/asset.service.dart';
|
||||||
import 'package:immich_mobile/services/share.service.dart';
|
import 'package:immich_mobile/services/share.service.dart';
|
||||||
|
@ -2,9 +2,9 @@ import 'package:easy_localization/easy_localization.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/entities/exif_info.entity.dart';
|
import 'package:immich_mobile/domain/models/exif.model.dart';
|
||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
|
||||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||||
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
import 'package:immich_mobile/extensions/theme_extensions.dart';
|
import 'package:immich_mobile/extensions/theme_extensions.dart';
|
||||||
import 'package:immich_mobile/providers/asset.provider.dart';
|
import 'package:immich_mobile/providers/asset.provider.dart';
|
||||||
import 'package:immich_mobile/providers/user.provider.dart';
|
import 'package:immich_mobile/providers/user.provider.dart';
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/domain/models/exif.model.dart';
|
||||||
|
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
import 'package:immich_mobile/providers/asset.provider.dart';
|
import 'package:immich_mobile/providers/asset.provider.dart';
|
||||||
import 'package:immich_mobile/widgets/asset_viewer/detail_panel/file_info.dart';
|
|
||||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
|
||||||
import 'package:immich_mobile/entities/exif_info.entity.dart';
|
|
||||||
import 'package:immich_mobile/widgets/asset_viewer/detail_panel/camera_info.dart';
|
import 'package:immich_mobile/widgets/asset_viewer/detail_panel/camera_info.dart';
|
||||||
|
import 'package:immich_mobile/widgets/asset_viewer/detail_panel/file_info.dart';
|
||||||
|
|
||||||
class AssetDetails extends ConsumerWidget {
|
class AssetDetails extends ConsumerWidget {
|
||||||
final Asset asset;
|
final Asset asset;
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/domain/models/exif.model.dart';
|
||||||
|
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
import 'package:immich_mobile/providers/asset.provider.dart';
|
import 'package:immich_mobile/providers/asset.provider.dart';
|
||||||
import 'package:immich_mobile/utils/selection_handlers.dart';
|
import 'package:immich_mobile/utils/selection_handlers.dart';
|
||||||
import 'package:immich_mobile/widgets/asset_viewer/detail_panel/exif_map.dart';
|
import 'package:immich_mobile/widgets/asset_viewer/detail_panel/exif_map.dart';
|
||||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
|
||||||
import 'package:immich_mobile/entities/exif_info.entity.dart';
|
|
||||||
|
|
||||||
class AssetLocation extends HookConsumerWidget {
|
class AssetLocation extends HookConsumerWidget {
|
||||||
final Asset asset;
|
final Asset asset;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:immich_mobile/entities/exif_info.entity.dart';
|
import 'package:immich_mobile/domain/models/exif.model.dart';
|
||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
|
|
||||||
class CameraInfo extends StatelessWidget {
|
class CameraInfo extends StatelessWidget {
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:immich_mobile/domain/models/exif.model.dart';
|
||||||
import 'package:immich_mobile/widgets/map/map_thumbnail.dart';
|
import 'package:immich_mobile/widgets/map/map_thumbnail.dart';
|
||||||
import 'package:immich_mobile/entities/exif_info.entity.dart';
|
|
||||||
import 'package:maplibre_gl/maplibre_gl.dart';
|
import 'package:maplibre_gl/maplibre_gl.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:immich_mobile/extensions/asset_extensions.dart';
|
import 'package:immich_mobile/domain/models/exif.model.dart';
|
||||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||||
import 'package:immich_mobile/entities/exif_info.entity.dart';
|
import 'package:immich_mobile/extensions/asset_extensions.dart';
|
||||||
import 'package:timezone/data/latest.dart';
|
import 'package:timezone/data/latest.dart';
|
||||||
import 'package:timezone/timezone.dart';
|
import 'package:timezone/timezone.dart';
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import 'package:immich_mobile/domain/interfaces/exif.interface.dart';
|
||||||
import 'package:immich_mobile/interfaces/album.interface.dart';
|
import 'package:immich_mobile/interfaces/album.interface.dart';
|
||||||
import 'package:immich_mobile/interfaces/album_api.interface.dart';
|
import 'package:immich_mobile/interfaces/album_api.interface.dart';
|
||||||
import 'package:immich_mobile/interfaces/album_media.interface.dart';
|
import 'package:immich_mobile/interfaces/album_media.interface.dart';
|
||||||
@ -7,7 +8,6 @@ import 'package:immich_mobile/interfaces/auth.interface.dart';
|
|||||||
import 'package:immich_mobile/interfaces/auth_api.interface.dart';
|
import 'package:immich_mobile/interfaces/auth_api.interface.dart';
|
||||||
import 'package:immich_mobile/interfaces/backup_album.interface.dart';
|
import 'package:immich_mobile/interfaces/backup_album.interface.dart';
|
||||||
import 'package:immich_mobile/interfaces/etag.interface.dart';
|
import 'package:immich_mobile/interfaces/etag.interface.dart';
|
||||||
import 'package:immich_mobile/interfaces/exif_info.interface.dart';
|
|
||||||
import 'package:immich_mobile/interfaces/file_media.interface.dart';
|
import 'package:immich_mobile/interfaces/file_media.interface.dart';
|
||||||
import 'package:immich_mobile/interfaces/user.interface.dart';
|
import 'package:immich_mobile/interfaces/user.interface.dart';
|
||||||
import 'package:mocktail/mocktail.dart';
|
import 'package:mocktail/mocktail.dart';
|
||||||
|
@ -11,9 +11,9 @@ import 'package:immich_mobile/entities/asset.entity.dart';
|
|||||||
import 'package:immich_mobile/entities/backup_album.entity.dart';
|
import 'package:immich_mobile/entities/backup_album.entity.dart';
|
||||||
import 'package:immich_mobile/entities/duplicated_asset.entity.dart';
|
import 'package:immich_mobile/entities/duplicated_asset.entity.dart';
|
||||||
import 'package:immich_mobile/entities/etag.entity.dart';
|
import 'package:immich_mobile/entities/etag.entity.dart';
|
||||||
import 'package:immich_mobile/entities/exif_info.entity.dart';
|
|
||||||
import 'package:immich_mobile/entities/ios_device_asset.entity.dart';
|
import 'package:immich_mobile/entities/ios_device_asset.entity.dart';
|
||||||
import 'package:immich_mobile/entities/user.entity.dart';
|
import 'package:immich_mobile/entities/user.entity.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/entities/exif.entity.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/log.entity.dart';
|
import 'package:immich_mobile/infrastructure/entities/log.entity.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/store.entity.dart';
|
import 'package:immich_mobile/infrastructure/entities/store.entity.dart';
|
||||||
import 'package:isar/isar.dart';
|
import 'package:isar/isar.dart';
|
||||||
|
Loading…
x
Reference in New Issue
Block a user