feat: sync stream repo impl for assets and exif

This commit is contained in:
shenlong-tanwen 2025-04-21 16:19:22 +05:30
parent 5fc1a63810
commit b44cfe5d1c
18 changed files with 2907 additions and 164 deletions

File diff suppressed because one or more lines are too long

View file

@ -1,3 +1,5 @@
import 'package:immich_mobile/utils/nullable_value.dart';
part 'local_asset.model.dart';
part 'merged_asset.model.dart';
part 'remote_asset.model.dart';
@ -16,8 +18,6 @@ sealed class Asset {
final AssetType type;
final DateTime createdAt;
final DateTime updatedAt;
final int? width;
final int? height;
final int? durationInSeconds;
const Asset({
@ -26,8 +26,6 @@ sealed class Asset {
required this.type,
required this.createdAt,
required this.updatedAt,
this.width,
this.height,
this.durationInSeconds,
});
@ -38,8 +36,6 @@ sealed class Asset {
type: $type,
createdAt: $createdAt,
updatedAt: $updatedAt,
width: ${width ?? "<NA>"},
height: ${height ?? "<NA>"},
durationInSeconds: ${durationInSeconds ?? "<NA>"}
}''';
}
@ -52,8 +48,6 @@ sealed class Asset {
type == other.type &&
createdAt == other.createdAt &&
updatedAt == other.updatedAt &&
width == other.width &&
height == other.height &&
durationInSeconds == other.durationInSeconds;
}
return false;
@ -65,8 +59,6 @@ sealed class Asset {
type.hashCode ^
createdAt.hashCode ^
updatedAt.hashCode ^
width.hashCode ^
height.hashCode ^
durationInSeconds.hashCode;
}
}

View file

@ -2,6 +2,8 @@ part of 'asset.model.dart';
class LocalAsset extends Asset {
final String localId;
final int? width;
final int? height;
const LocalAsset({
required this.localId,
@ -10,8 +12,8 @@ class LocalAsset extends Asset {
required super.type,
required super.createdAt,
required super.updatedAt,
super.width,
super.height,
this.width,
this.height,
super.durationInSeconds,
});
@ -33,7 +35,10 @@ class LocalAsset extends Asset {
bool operator ==(Object other) {
if (other is! LocalAsset) return false;
if (identical(this, other)) return true;
return super == other && localId == other.localId;
return super == other &&
localId == other.localId &&
width == other.width &&
height == other.height;
}
@override
@ -48,9 +53,9 @@ class LocalAsset extends Asset {
AssetType? type,
DateTime? createdAt,
DateTime? updatedAt,
int? width,
int? height,
int? durationInSeconds,
NullableValue<int> width = const NullableValue.absent(),
NullableValue<int> height = const NullableValue.absent(),
NullableValue<int> durationInSeconds = const NullableValue.absent(),
}) {
return LocalAsset(
localId: localId ?? this.localId,
@ -59,9 +64,9 @@ class LocalAsset extends Asset {
type: type ?? this.type,
createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt,
width: width ?? this.width,
height: height ?? this.height,
durationInSeconds: durationInSeconds ?? this.durationInSeconds,
width: width.getOrDefault(this.width),
height: height.getOrDefault(this.height),
durationInSeconds: durationInSeconds.getOrDefault(this.durationInSeconds),
);
}
}

View file

@ -12,8 +12,6 @@ class MergedAsset extends Asset {
required super.type,
required super.createdAt,
required super.updatedAt,
super.width,
super.height,
super.durationInSeconds,
});
@ -26,8 +24,6 @@ class MergedAsset extends Asset {
type: $type,
createdAt: $createdAt,
updatedAt: $updatedAt,
width: ${width ?? "<NA>"},
height: ${height ?? "<NA>"},
durationInSeconds: ${durationInSeconds ?? "<NA>"}
}''';
}

View file

@ -2,16 +2,24 @@ part of 'asset.model.dart';
class RemoteAsset extends Asset {
final String remoteId;
final bool isFavorite;
final String ownerId;
final DateTime localDateTime;
final String? thumbhash;
final DateTime? deletedAt;
const RemoteAsset({
required this.remoteId,
required this.ownerId,
required super.name,
required super.checksum,
required super.type,
required this.isFavorite,
required this.localDateTime,
required super.createdAt,
required super.updatedAt,
super.width,
super.height,
this.deletedAt,
this.thumbhash,
super.durationInSeconds,
});
@ -19,13 +27,15 @@ class RemoteAsset extends Asset {
String toString() {
return '''RemoteAsset {
remoteId: $remoteId,
ownerId: $ownerId,
name: $name,
type: $type,
isFavorite: $isFavorite,
createdAt: $createdAt,
updatedAt: $updatedAt,
width: ${width ?? "<NA>"},
height: ${height ?? "<NA>"},
durationInSeconds: ${durationInSeconds ?? "<NA>"}
localDateTime: $localDateTime,
deletedAt: ${deletedAt ?? "<NA>"},
durationInSeconds: ${durationInSeconds ?? "<NA>"},
}''';
}
@ -33,11 +43,23 @@ class RemoteAsset extends Asset {
bool operator ==(Object other) {
if (other is! RemoteAsset) return false;
if (identical(this, other)) return true;
return super == other && remoteId == other.remoteId;
return super == other &&
remoteId == other.remoteId &&
isFavorite == other.isFavorite &&
ownerId == other.ownerId &&
localDateTime == other.localDateTime &&
deletedAt == other.deletedAt &&
thumbhash == other.thumbhash;
}
@override
int get hashCode {
return super.hashCode ^ remoteId.hashCode;
return super.hashCode ^
remoteId.hashCode ^
isFavorite.hashCode ^
ownerId.hashCode ^
localDateTime.hashCode ^
deletedAt.hashCode ^
thumbhash.hashCode;
}
}

View file

@ -32,7 +32,7 @@ class LocalAlbum {
String? name,
DateTime? updatedAt,
int? assetCount,
NullableValue<String>? thumbnailId,
NullableValue<String> thumbnailId = const NullableValue.absent(),
BackupSelection? backupSelection,
bool? isAll,
}) {
@ -41,7 +41,7 @@ class LocalAlbum {
name: name ?? this.name,
updatedAt: updatedAt ?? this.updatedAt,
assetCount: assetCount ?? this.assetCount,
thumbnailId: thumbnailId?.getOrDefault(this.thumbnailId),
thumbnailId: thumbnailId.getOrDefault(this.thumbnailId),
backupSelection: backupSelection ?? this.backupSelection,
isAll: isAll ?? this.isAll,
);

View file

@ -1,4 +1,7 @@
import 'package:drift/drift.dart' hide Query;
import 'package:immich_mobile/domain/models/exif.model.dart' as domain;
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.dart';
import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart';
import 'package:immich_mobile/infrastructure/utils/exif.converter.dart';
import 'package:isar/isar.dart';
@ -90,3 +93,53 @@ class ExifInfo {
exposureSeconds: exposureSeconds,
);
}
class ExifEntity extends Table with DriftDefaultsMixin {
const ExifEntity();
BlobColumn get assetId => blob()
.references(RemoteAssetEntity, #remoteId, onDelete: KeyAction.cascade)();
TextColumn get city => text().nullable()();
TextColumn get state => text().nullable()();
TextColumn get country => text().nullable()();
DateTimeColumn get dateTimeOriginal => dateTime().nullable()();
TextColumn get description => text().nullable()();
IntColumn get height => integer().nullable()();
IntColumn get width => integer().nullable()();
TextColumn get exposureTime => text().nullable()();
IntColumn get fNumber => integer().nullable()();
IntColumn get fileSize => integer().nullable()();
IntColumn get focalLength => integer().nullable()();
IntColumn get latitude => integer().nullable()();
IntColumn get longitude => integer().nullable()();
IntColumn get iso => integer().nullable()();
TextColumn get make => text().nullable()();
TextColumn get model => text().nullable()();
TextColumn get orientation => text().nullable()();
TextColumn get timeZone => text().nullable()();
IntColumn get rating => integer().nullable()();
TextColumn get projectionType => text().nullable()();
@override
Set<Column> get primaryKey => {assetId};
}

File diff suppressed because it is too large Load diff

View file

@ -12,6 +12,10 @@ class LocalAssetEntity extends Table with DriftDefaultsMixin, AssetEntityMixin {
TextColumn get checksum => text().nullable()();
IntColumn get width => integer().nullable()();
IntColumn get height => integer().nullable()();
@override
Set<Column> get primaryKey => {localId};
}

View file

@ -14,11 +14,11 @@ typedef $$LocalAssetEntityTableCreateCompanionBuilder
required i2.AssetType type,
i0.Value<DateTime> createdAt,
i0.Value<DateTime> updatedAt,
i0.Value<int?> width,
i0.Value<int?> height,
i0.Value<int?> durationInSeconds,
required String localId,
i0.Value<String?> checksum,
i0.Value<int?> width,
i0.Value<int?> height,
});
typedef $$LocalAssetEntityTableUpdateCompanionBuilder
= i1.LocalAssetEntityCompanion Function({
@ -26,11 +26,11 @@ typedef $$LocalAssetEntityTableUpdateCompanionBuilder
i0.Value<i2.AssetType> type,
i0.Value<DateTime> createdAt,
i0.Value<DateTime> updatedAt,
i0.Value<int?> width,
i0.Value<int?> height,
i0.Value<int?> durationInSeconds,
i0.Value<String> localId,
i0.Value<String?> checksum,
i0.Value<int?> width,
i0.Value<int?> height,
});
class $$LocalAssetEntityTableFilterComposer
@ -56,12 +56,6 @@ class $$LocalAssetEntityTableFilterComposer
i0.ColumnFilters<DateTime> get updatedAt => $composableBuilder(
column: $table.updatedAt, builder: (column) => i0.ColumnFilters(column));
i0.ColumnFilters<int> get width => $composableBuilder(
column: $table.width, builder: (column) => i0.ColumnFilters(column));
i0.ColumnFilters<int> get height => $composableBuilder(
column: $table.height, builder: (column) => i0.ColumnFilters(column));
i0.ColumnFilters<int> get durationInSeconds => $composableBuilder(
column: $table.durationInSeconds,
builder: (column) => i0.ColumnFilters(column));
@ -71,6 +65,12 @@ class $$LocalAssetEntityTableFilterComposer
i0.ColumnFilters<String> get checksum => $composableBuilder(
column: $table.checksum, builder: (column) => i0.ColumnFilters(column));
i0.ColumnFilters<int> get width => $composableBuilder(
column: $table.width, builder: (column) => i0.ColumnFilters(column));
i0.ColumnFilters<int> get height => $composableBuilder(
column: $table.height, builder: (column) => i0.ColumnFilters(column));
}
class $$LocalAssetEntityTableOrderingComposer
@ -96,12 +96,6 @@ class $$LocalAssetEntityTableOrderingComposer
column: $table.updatedAt,
builder: (column) => i0.ColumnOrderings(column));
i0.ColumnOrderings<int> get width => $composableBuilder(
column: $table.width, builder: (column) => i0.ColumnOrderings(column));
i0.ColumnOrderings<int> get height => $composableBuilder(
column: $table.height, builder: (column) => i0.ColumnOrderings(column));
i0.ColumnOrderings<int> get durationInSeconds => $composableBuilder(
column: $table.durationInSeconds,
builder: (column) => i0.ColumnOrderings(column));
@ -111,6 +105,12 @@ class $$LocalAssetEntityTableOrderingComposer
i0.ColumnOrderings<String> get checksum => $composableBuilder(
column: $table.checksum, builder: (column) => i0.ColumnOrderings(column));
i0.ColumnOrderings<int> get width => $composableBuilder(
column: $table.width, builder: (column) => i0.ColumnOrderings(column));
i0.ColumnOrderings<int> get height => $composableBuilder(
column: $table.height, builder: (column) => i0.ColumnOrderings(column));
}
class $$LocalAssetEntityTableAnnotationComposer
@ -134,12 +134,6 @@ class $$LocalAssetEntityTableAnnotationComposer
i0.GeneratedColumn<DateTime> get updatedAt =>
$composableBuilder(column: $table.updatedAt, builder: (column) => column);
i0.GeneratedColumn<int> get width =>
$composableBuilder(column: $table.width, builder: (column) => column);
i0.GeneratedColumn<int> get height =>
$composableBuilder(column: $table.height, builder: (column) => column);
i0.GeneratedColumn<int> get durationInSeconds => $composableBuilder(
column: $table.durationInSeconds, builder: (column) => column);
@ -148,6 +142,12 @@ class $$LocalAssetEntityTableAnnotationComposer
i0.GeneratedColumn<String> get checksum =>
$composableBuilder(column: $table.checksum, builder: (column) => column);
i0.GeneratedColumn<int> get width =>
$composableBuilder(column: $table.width, builder: (column) => column);
i0.GeneratedColumn<int> get height =>
$composableBuilder(column: $table.height, builder: (column) => column);
}
class $$LocalAssetEntityTableTableManager extends i0.RootTableManager<
@ -183,44 +183,44 @@ class $$LocalAssetEntityTableTableManager extends i0.RootTableManager<
i0.Value<i2.AssetType> type = const i0.Value.absent(),
i0.Value<DateTime> createdAt = const i0.Value.absent(),
i0.Value<DateTime> updatedAt = const i0.Value.absent(),
i0.Value<int?> width = const i0.Value.absent(),
i0.Value<int?> height = const i0.Value.absent(),
i0.Value<int?> durationInSeconds = const i0.Value.absent(),
i0.Value<String> localId = const i0.Value.absent(),
i0.Value<String?> checksum = const i0.Value.absent(),
i0.Value<int?> width = const i0.Value.absent(),
i0.Value<int?> height = const i0.Value.absent(),
}) =>
i1.LocalAssetEntityCompanion(
name: name,
type: type,
createdAt: createdAt,
updatedAt: updatedAt,
width: width,
height: height,
durationInSeconds: durationInSeconds,
localId: localId,
checksum: checksum,
width: width,
height: height,
),
createCompanionCallback: ({
required String name,
required i2.AssetType type,
i0.Value<DateTime> createdAt = const i0.Value.absent(),
i0.Value<DateTime> updatedAt = const i0.Value.absent(),
i0.Value<int?> width = const i0.Value.absent(),
i0.Value<int?> height = const i0.Value.absent(),
i0.Value<int?> durationInSeconds = const i0.Value.absent(),
required String localId,
i0.Value<String?> checksum = const i0.Value.absent(),
i0.Value<int?> width = const i0.Value.absent(),
i0.Value<int?> height = const i0.Value.absent(),
}) =>
i1.LocalAssetEntityCompanion.insert(
name: name,
type: type,
createdAt: createdAt,
updatedAt: updatedAt,
width: width,
height: height,
durationInSeconds: durationInSeconds,
localId: localId,
checksum: checksum,
width: width,
height: height,
),
withReferenceMapper: (p0) => p0
.map((e) => (e.readTable(table), i0.BaseReferences(db, table, e)))
@ -282,18 +282,6 @@ class $LocalAssetEntityTable extends i3.LocalAssetEntity
type: i0.DriftSqlType.dateTime,
requiredDuringInsert: false,
defaultValue: i4.currentDateAndTime);
static const i0.VerificationMeta _widthMeta =
const i0.VerificationMeta('width');
@override
late final i0.GeneratedColumn<int> width = i0.GeneratedColumn<int>(
'width', aliasedName, true,
type: i0.DriftSqlType.int, requiredDuringInsert: false);
static const i0.VerificationMeta _heightMeta =
const i0.VerificationMeta('height');
@override
late final i0.GeneratedColumn<int> height = i0.GeneratedColumn<int>(
'height', aliasedName, true,
type: i0.DriftSqlType.int, requiredDuringInsert: false);
static const i0.VerificationMeta _durationInSecondsMeta =
const i0.VerificationMeta('durationInSeconds');
@override
@ -312,17 +300,29 @@ class $LocalAssetEntityTable extends i3.LocalAssetEntity
late final i0.GeneratedColumn<String> checksum = i0.GeneratedColumn<String>(
'checksum', aliasedName, true,
type: i0.DriftSqlType.string, requiredDuringInsert: false);
static const i0.VerificationMeta _widthMeta =
const i0.VerificationMeta('width');
@override
late final i0.GeneratedColumn<int> width = i0.GeneratedColumn<int>(
'width', aliasedName, true,
type: i0.DriftSqlType.int, requiredDuringInsert: false);
static const i0.VerificationMeta _heightMeta =
const i0.VerificationMeta('height');
@override
late final i0.GeneratedColumn<int> height = i0.GeneratedColumn<int>(
'height', aliasedName, true,
type: i0.DriftSqlType.int, requiredDuringInsert: false);
@override
List<i0.GeneratedColumn> get $columns => [
name,
type,
createdAt,
updatedAt,
width,
height,
durationInSeconds,
localId,
checksum
checksum,
width,
height
];
@override
String get aliasedName => _alias ?? actualTableName;
@ -349,14 +349,6 @@ class $LocalAssetEntityTable extends i3.LocalAssetEntity
context.handle(_updatedAtMeta,
updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta));
}
if (data.containsKey('width')) {
context.handle(
_widthMeta, width.isAcceptableOrUnknown(data['width']!, _widthMeta));
}
if (data.containsKey('height')) {
context.handle(_heightMeta,
height.isAcceptableOrUnknown(data['height']!, _heightMeta));
}
if (data.containsKey('duration_in_seconds')) {
context.handle(
_durationInSecondsMeta,
@ -373,6 +365,14 @@ class $LocalAssetEntityTable extends i3.LocalAssetEntity
context.handle(_checksumMeta,
checksum.isAcceptableOrUnknown(data['checksum']!, _checksumMeta));
}
if (data.containsKey('width')) {
context.handle(
_widthMeta, width.isAcceptableOrUnknown(data['width']!, _widthMeta));
}
if (data.containsKey('height')) {
context.handle(_heightMeta,
height.isAcceptableOrUnknown(data['height']!, _heightMeta));
}
return context;
}
@ -392,16 +392,16 @@ class $LocalAssetEntityTable extends i3.LocalAssetEntity
i0.DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!,
updatedAt: attachedDatabase.typeMapping.read(
i0.DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!,
width: attachedDatabase.typeMapping
.read(i0.DriftSqlType.int, data['${effectivePrefix}width']),
height: attachedDatabase.typeMapping
.read(i0.DriftSqlType.int, data['${effectivePrefix}height']),
durationInSeconds: attachedDatabase.typeMapping.read(
i0.DriftSqlType.int, data['${effectivePrefix}duration_in_seconds']),
localId: attachedDatabase.typeMapping
.read(i0.DriftSqlType.string, data['${effectivePrefix}local_id'])!,
checksum: attachedDatabase.typeMapping
.read(i0.DriftSqlType.string, data['${effectivePrefix}checksum']),
width: attachedDatabase.typeMapping
.read(i0.DriftSqlType.int, data['${effectivePrefix}width']),
height: attachedDatabase.typeMapping
.read(i0.DriftSqlType.int, data['${effectivePrefix}height']),
);
}
@ -424,21 +424,21 @@ class LocalAssetEntityData extends i0.DataClass
final i2.AssetType type;
final DateTime createdAt;
final DateTime updatedAt;
final int? width;
final int? height;
final int? durationInSeconds;
final String localId;
final String? checksum;
final int? width;
final int? height;
const LocalAssetEntityData(
{required this.name,
required this.type,
required this.createdAt,
required this.updatedAt,
this.width,
this.height,
this.durationInSeconds,
required this.localId,
this.checksum});
this.checksum,
this.width,
this.height});
@override
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
final map = <String, i0.Expression>{};
@ -449,12 +449,6 @@ class LocalAssetEntityData extends i0.DataClass
}
map['created_at'] = i0.Variable<DateTime>(createdAt);
map['updated_at'] = i0.Variable<DateTime>(updatedAt);
if (!nullToAbsent || width != null) {
map['width'] = i0.Variable<int>(width);
}
if (!nullToAbsent || height != null) {
map['height'] = i0.Variable<int>(height);
}
if (!nullToAbsent || durationInSeconds != null) {
map['duration_in_seconds'] = i0.Variable<int>(durationInSeconds);
}
@ -462,6 +456,12 @@ class LocalAssetEntityData extends i0.DataClass
if (!nullToAbsent || checksum != null) {
map['checksum'] = i0.Variable<String>(checksum);
}
if (!nullToAbsent || width != null) {
map['width'] = i0.Variable<int>(width);
}
if (!nullToAbsent || height != null) {
map['height'] = i0.Variable<int>(height);
}
return map;
}
@ -474,11 +474,11 @@ class LocalAssetEntityData extends i0.DataClass
.fromJson(serializer.fromJson<int>(json['type'])),
createdAt: serializer.fromJson<DateTime>(json['createdAt']),
updatedAt: serializer.fromJson<DateTime>(json['updatedAt']),
width: serializer.fromJson<int?>(json['width']),
height: serializer.fromJson<int?>(json['height']),
durationInSeconds: serializer.fromJson<int?>(json['durationInSeconds']),
localId: serializer.fromJson<String>(json['localId']),
checksum: serializer.fromJson<String?>(json['checksum']),
width: serializer.fromJson<int?>(json['width']),
height: serializer.fromJson<int?>(json['height']),
);
}
@override
@ -490,11 +490,11 @@ class LocalAssetEntityData extends i0.DataClass
.toJson<int>(i1.$LocalAssetEntityTable.$convertertype.toJson(type)),
'createdAt': serializer.toJson<DateTime>(createdAt),
'updatedAt': serializer.toJson<DateTime>(updatedAt),
'width': serializer.toJson<int?>(width),
'height': serializer.toJson<int?>(height),
'durationInSeconds': serializer.toJson<int?>(durationInSeconds),
'localId': serializer.toJson<String>(localId),
'checksum': serializer.toJson<String?>(checksum),
'width': serializer.toJson<int?>(width),
'height': serializer.toJson<int?>(height),
};
}
@ -503,23 +503,23 @@ class LocalAssetEntityData extends i0.DataClass
i2.AssetType? type,
DateTime? createdAt,
DateTime? updatedAt,
i0.Value<int?> width = const i0.Value.absent(),
i0.Value<int?> height = const i0.Value.absent(),
i0.Value<int?> durationInSeconds = const i0.Value.absent(),
String? localId,
i0.Value<String?> checksum = const i0.Value.absent()}) =>
i0.Value<String?> checksum = const i0.Value.absent(),
i0.Value<int?> width = const i0.Value.absent(),
i0.Value<int?> height = const i0.Value.absent()}) =>
i1.LocalAssetEntityData(
name: name ?? this.name,
type: type ?? this.type,
createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt,
width: width.present ? width.value : this.width,
height: height.present ? height.value : this.height,
durationInSeconds: durationInSeconds.present
? durationInSeconds.value
: this.durationInSeconds,
localId: localId ?? this.localId,
checksum: checksum.present ? checksum.value : this.checksum,
width: width.present ? width.value : this.width,
height: height.present ? height.value : this.height,
);
LocalAssetEntityData copyWithCompanion(i1.LocalAssetEntityCompanion data) {
return LocalAssetEntityData(
@ -527,13 +527,13 @@ class LocalAssetEntityData extends i0.DataClass
type: data.type.present ? data.type.value : this.type,
createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt,
updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt,
width: data.width.present ? data.width.value : this.width,
height: data.height.present ? data.height.value : this.height,
durationInSeconds: data.durationInSeconds.present
? data.durationInSeconds.value
: this.durationInSeconds,
localId: data.localId.present ? data.localId.value : this.localId,
checksum: data.checksum.present ? data.checksum.value : this.checksum,
width: data.width.present ? data.width.value : this.width,
height: data.height.present ? data.height.value : this.height,
);
}
@ -544,18 +544,18 @@ class LocalAssetEntityData extends i0.DataClass
..write('type: $type, ')
..write('createdAt: $createdAt, ')
..write('updatedAt: $updatedAt, ')
..write('width: $width, ')
..write('height: $height, ')
..write('durationInSeconds: $durationInSeconds, ')
..write('localId: $localId, ')
..write('checksum: $checksum')
..write('checksum: $checksum, ')
..write('width: $width, ')
..write('height: $height')
..write(')'))
.toString();
}
@override
int get hashCode => Object.hash(name, type, createdAt, updatedAt, width,
height, durationInSeconds, localId, checksum);
int get hashCode => Object.hash(name, type, createdAt, updatedAt,
durationInSeconds, localId, checksum, width, height);
@override
bool operator ==(Object other) =>
identical(this, other) ||
@ -564,11 +564,11 @@ class LocalAssetEntityData extends i0.DataClass
other.type == this.type &&
other.createdAt == this.createdAt &&
other.updatedAt == this.updatedAt &&
other.width == this.width &&
other.height == this.height &&
other.durationInSeconds == this.durationInSeconds &&
other.localId == this.localId &&
other.checksum == this.checksum);
other.checksum == this.checksum &&
other.width == this.width &&
other.height == this.height);
}
class LocalAssetEntityCompanion
@ -577,32 +577,32 @@ class LocalAssetEntityCompanion
final i0.Value<i2.AssetType> type;
final i0.Value<DateTime> createdAt;
final i0.Value<DateTime> updatedAt;
final i0.Value<int?> width;
final i0.Value<int?> height;
final i0.Value<int?> durationInSeconds;
final i0.Value<String> localId;
final i0.Value<String?> checksum;
final i0.Value<int?> width;
final i0.Value<int?> height;
const LocalAssetEntityCompanion({
this.name = const i0.Value.absent(),
this.type = const i0.Value.absent(),
this.createdAt = const i0.Value.absent(),
this.updatedAt = const i0.Value.absent(),
this.width = const i0.Value.absent(),
this.height = const i0.Value.absent(),
this.durationInSeconds = const i0.Value.absent(),
this.localId = const i0.Value.absent(),
this.checksum = const i0.Value.absent(),
this.width = const i0.Value.absent(),
this.height = const i0.Value.absent(),
});
LocalAssetEntityCompanion.insert({
required String name,
required i2.AssetType type,
this.createdAt = const i0.Value.absent(),
this.updatedAt = const i0.Value.absent(),
this.width = const i0.Value.absent(),
this.height = const i0.Value.absent(),
this.durationInSeconds = const i0.Value.absent(),
required String localId,
this.checksum = const i0.Value.absent(),
this.width = const i0.Value.absent(),
this.height = const i0.Value.absent(),
}) : name = i0.Value(name),
type = i0.Value(type),
localId = i0.Value(localId);
@ -611,22 +611,22 @@ class LocalAssetEntityCompanion
i0.Expression<int>? type,
i0.Expression<DateTime>? createdAt,
i0.Expression<DateTime>? updatedAt,
i0.Expression<int>? width,
i0.Expression<int>? height,
i0.Expression<int>? durationInSeconds,
i0.Expression<String>? localId,
i0.Expression<String>? checksum,
i0.Expression<int>? width,
i0.Expression<int>? height,
}) {
return i0.RawValuesInsertable({
if (name != null) 'name': name,
if (type != null) 'type': type,
if (createdAt != null) 'created_at': createdAt,
if (updatedAt != null) 'updated_at': updatedAt,
if (width != null) 'width': width,
if (height != null) 'height': height,
if (durationInSeconds != null) 'duration_in_seconds': durationInSeconds,
if (localId != null) 'local_id': localId,
if (checksum != null) 'checksum': checksum,
if (width != null) 'width': width,
if (height != null) 'height': height,
});
}
@ -635,21 +635,21 @@ class LocalAssetEntityCompanion
i0.Value<i2.AssetType>? type,
i0.Value<DateTime>? createdAt,
i0.Value<DateTime>? updatedAt,
i0.Value<int?>? width,
i0.Value<int?>? height,
i0.Value<int?>? durationInSeconds,
i0.Value<String>? localId,
i0.Value<String?>? checksum}) {
i0.Value<String?>? checksum,
i0.Value<int?>? width,
i0.Value<int?>? height}) {
return i1.LocalAssetEntityCompanion(
name: name ?? this.name,
type: type ?? this.type,
createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt,
width: width ?? this.width,
height: height ?? this.height,
durationInSeconds: durationInSeconds ?? this.durationInSeconds,
localId: localId ?? this.localId,
checksum: checksum ?? this.checksum,
width: width ?? this.width,
height: height ?? this.height,
);
}
@ -669,12 +669,6 @@ class LocalAssetEntityCompanion
if (updatedAt.present) {
map['updated_at'] = i0.Variable<DateTime>(updatedAt.value);
}
if (width.present) {
map['width'] = i0.Variable<int>(width.value);
}
if (height.present) {
map['height'] = i0.Variable<int>(height.value);
}
if (durationInSeconds.present) {
map['duration_in_seconds'] = i0.Variable<int>(durationInSeconds.value);
}
@ -684,6 +678,12 @@ class LocalAssetEntityCompanion
if (checksum.present) {
map['checksum'] = i0.Variable<String>(checksum.value);
}
if (width.present) {
map['width'] = i0.Variable<int>(width.value);
}
if (height.present) {
map['height'] = i0.Variable<int>(height.value);
}
return map;
}
@ -694,11 +694,11 @@ class LocalAssetEntityCompanion
..write('type: $type, ')
..write('createdAt: $createdAt, ')
..write('updatedAt: $updatedAt, ')
..write('width: $width, ')
..write('height: $height, ')
..write('durationInSeconds: $durationInSeconds, ')
..write('localId: $localId, ')
..write('checksum: $checksum')
..write('checksum: $checksum, ')
..write('width: $width, ')
..write('height: $height')
..write(')'))
.toString();
}

View file

@ -0,0 +1,28 @@
import 'package:drift/drift.dart';
import 'package:immich_mobile/infrastructure/entities/user.entity.dart';
import 'package:immich_mobile/infrastructure/utils/asset.mixin.dart';
import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart';
@TableIndex(name: 'remote_asset_checksum', columns: {#checksum})
class RemoteAssetEntity extends Table
with DriftDefaultsMixin, AssetEntityMixin {
const RemoteAssetEntity();
BlobColumn get remoteId => blob()();
TextColumn get checksum => text().unique()();
BoolColumn get isFavorite => boolean().withDefault(const Constant(false))();
BlobColumn get ownerId =>
blob().references(UserEntity, #id, onDelete: KeyAction.cascade)();
DateTimeColumn get localDateTime => dateTime().nullable()();
TextColumn get thumbhash => text().nullable()();
DateTimeColumn get deletedAt => dateTime().nullable()();
@override
Set<Column> get primaryKey => {remoteId};
}

File diff suppressed because it is too large Load diff

View file

@ -3,10 +3,12 @@ import 'dart:async';
import 'package:drift/drift.dart';
import 'package:drift_flutter/drift_flutter.dart';
import 'package:immich_mobile/domain/interfaces/db.interface.dart';
import 'package:immich_mobile/infrastructure/entities/exif.entity.dart';
import 'package:immich_mobile/infrastructure/entities/local_album.entity.dart';
import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.dart';
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart';
import 'package:immich_mobile/infrastructure/entities/partner.entity.dart';
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.dart';
import 'package:immich_mobile/infrastructure/entities/user.entity.dart';
import 'package:immich_mobile/infrastructure/entities/user_metadata.entity.dart';
import 'package:isar/isar.dart';
@ -36,6 +38,8 @@ class IsarDatabaseRepository implements IDatabaseRepository {
LocalAlbumEntity,
LocalAssetEntity,
LocalAlbumAssetEntity,
RemoteAssetEntity,
ExifEntity,
],
)
class Drift extends $Drift implements IDatabaseRepository {

View file

@ -13,6 +13,10 @@ import 'package:immich_mobile/infrastructure/entities/local_album.entity.drift.d
as i5;
import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.drift.dart'
as i6;
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart'
as i7;
import 'package:immich_mobile/infrastructure/entities/exif.entity.drift.dart'
as i8;
abstract class $Drift extends i0.GeneratedDatabase {
$Drift(i0.QueryExecutor e) : super(e);
@ -28,6 +32,9 @@ abstract class $Drift extends i0.GeneratedDatabase {
i5.$LocalAlbumEntityTable(this);
late final i6.$LocalAlbumAssetEntityTable localAlbumAssetEntity =
i6.$LocalAlbumAssetEntityTable(this);
late final i7.$RemoteAssetEntityTable remoteAssetEntity =
i7.$RemoteAssetEntityTable(this);
late final i8.$ExifEntityTable exifEntity = i8.$ExifEntityTable(this);
@override
Iterable<i0.TableInfo<i0.Table, Object?>> get allTables =>
allSchemaEntities.whereType<i0.TableInfo<i0.Table, Object?>>();
@ -39,7 +46,10 @@ abstract class $Drift extends i0.GeneratedDatabase {
localAssetEntity,
localAlbumEntity,
localAlbumAssetEntity,
i4.localAssetChecksum
remoteAssetEntity,
exifEntity,
i4.localAssetChecksum,
i7.remoteAssetChecksum
];
@override
i0.StreamQueryUpdateRules get streamUpdateRules =>
@ -90,6 +100,20 @@ abstract class $Drift extends i0.GeneratedDatabase {
kind: i0.UpdateKind.delete),
],
),
i0.WritePropagation(
on: i0.TableUpdateQuery.onTableName('user_entity',
limitUpdateKind: i0.UpdateKind.delete),
result: [
i0.TableUpdate('remote_asset_entity', kind: i0.UpdateKind.delete),
],
),
i0.WritePropagation(
on: i0.TableUpdateQuery.onTableName('remote_asset_entity',
limitUpdateKind: i0.UpdateKind.delete),
result: [
i0.TableUpdate('exif_entity', kind: i0.UpdateKind.delete),
],
),
],
);
@override
@ -112,4 +136,8 @@ class $DriftManager {
i5.$$LocalAlbumEntityTableTableManager(_db, _db.localAlbumEntity);
i6.$$LocalAlbumAssetEntityTableTableManager get localAlbumAssetEntity => i6
.$$LocalAlbumAssetEntityTableTableManager(_db, _db.localAlbumAssetEntity);
i7.$$RemoteAssetEntityTableTableManager get remoteAssetEntity =>
i7.$$RemoteAssetEntityTableTableManager(_db, _db.remoteAssetEntity);
i8.$$ExifEntityTableTableManager get exifEntity =>
i8.$$ExifEntityTableTableManager(_db, _db.exifEntity);
}

View file

@ -1,8 +1,10 @@
import 'package:drift/drift.dart';
import 'package:flutter/foundation.dart';
import 'package:immich_mobile/domain/interfaces/sync_stream.interface.dart';
import 'package:immich_mobile/domain/models/asset/asset.model.dart';
import 'package:immich_mobile/extensions/string_extensions.dart';
import 'package:immich_mobile/infrastructure/entities/exif.entity.drift.dart';
import 'package:immich_mobile/infrastructure/entities/partner.entity.drift.dart';
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart';
import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart';
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
import 'package:logging/logging.dart';
@ -99,36 +101,144 @@ class DriftSyncStreamRepository extends DriftDatabaseRepository
}
}
// Assets
@override
Future<void> updateAssetsV1(Iterable<SyncAssetV1> data) async {
debugPrint("updateAssetsV1 - ${data.length}");
}
@override
Future<void> deleteAssetsV1(Iterable<SyncAssetDeleteV1> data) async {
debugPrint("deleteAssetsV1 - ${data.length}");
try {
await _deleteAssetsV1(data);
} catch (e, s) {
_logger.severe('Error while processing deleteAssetsV1', e, s);
rethrow;
}
}
// Partner Assets
@override
Future<void> updatePartnerAssetsV1(Iterable<SyncAssetV1> data) async {
debugPrint("updatePartnerAssetsV1 - ${data.length}");
Future<void> updateAssetsV1(Iterable<SyncAssetV1> data) async {
try {
await _updateAssetsV1(data);
} catch (e, s) {
_logger.severe('Error while processing updateAssetsV1', e, s);
rethrow;
}
}
@override
Future<void> deletePartnerAssetsV1(Iterable<SyncAssetDeleteV1> data) async {
debugPrint("deletePartnerAssetsV1 - ${data.length}");
try {
await _deleteAssetsV1(data);
} catch (e, s) {
_logger.severe('Error while processing deletePartnerAssetsV1', e, s);
rethrow;
}
}
@override
Future<void> updatePartnerAssetsV1(Iterable<SyncAssetV1> data) async {
try {
await _updateAssetsV1(data);
} catch (e, s) {
_logger.severe('Error while processing updatePartnerAssetsV1', e, s);
rethrow;
}
}
// EXIF
@override
Future<void> updateAssetsExifV1(Iterable<SyncAssetExifV1> data) async {
debugPrint("updateAssetsExifV1 - ${data.length}");
try {
await _updateAssetExifV1(data);
} catch (e, s) {
_logger.severe('Error while processing updateAssetsExifV1', e, s);
rethrow;
}
}
@override
Future<void> updatePartnerAssetsExifV1(Iterable<SyncAssetExifV1> data) async {
debugPrint("updatePartnerAssetsExifV1 - ${data.length}");
try {
await _updateAssetExifV1(data);
} catch (e, s) {
_logger.severe('Error while processing updatePartnerAssetsExifV1', e, s);
rethrow;
}
}
Future<void> _updateAssetsV1(Iterable<SyncAssetV1> data) =>
_db.batch((batch) {
for (final asset in data) {
final companion = RemoteAssetEntityCompanion(
name: const Value(''), // TODO: Needed from the server
type: Value(asset.type.toAssetType()),
createdAt: Value.absentIfNull(asset.fileCreatedAt),
updatedAt: Value.absentIfNull(asset.fileModifiedAt),
durationInSeconds: const Value(0),
checksum: Value(asset.checksum),
isFavorite: Value(asset.isFavorite),
ownerId: Value(asset.ownerId.toUuidByte()),
localDateTime: Value(asset.localDateTime),
thumbhash: Value(asset.thumbhash),
deletedAt: Value(asset.deletedAt),
);
batch.insert(
_db.remoteAssetEntity,
companion.copyWith(remoteId: Value(asset.id.toUuidByte())),
onConflict: DoUpdate((_) => companion),
);
}
});
Future<void> _deleteAssetsV1(Iterable<SyncAssetDeleteV1> assets) =>
_db.batch((batch) {
for (final asset in assets) {
batch.delete(
_db.remoteAssetEntity,
RemoteAssetEntityCompanion(
remoteId: Value(asset.assetId.toUuidByte()),
),
);
}
});
Future<void> _updateAssetExifV1(Iterable<SyncAssetExifV1> data) =>
_db.batch((batch) {
for (final exif in data) {
final companion = ExifEntityCompanion(
city: Value(exif.city),
state: Value(exif.state),
country: Value(exif.country),
dateTimeOriginal: Value(exif.dateTimeOriginal),
description: Value(exif.description),
height: Value(exif.exifImageHeight),
width: Value(exif.exifImageWidth),
exposureTime: Value(exif.exposureTime),
fNumber: Value(exif.fNumber),
fileSize: Value(exif.fileSizeInByte),
focalLength: Value(exif.focalLength),
latitude: Value(exif.latitude),
longitude: Value(exif.longitude),
iso: Value(exif.iso),
make: Value(exif.make),
model: Value(exif.model),
orientation: Value(exif.orientation),
timeZone: Value(exif.timeZone),
rating: Value(exif.rating),
projectionType: Value(exif.projectionType),
);
batch.insert(
_db.exifEntity,
companion.copyWith(assetId: Value(exif.assetId.toUuidByte())),
onConflict: DoUpdate((_) => companion),
);
}
});
}
extension on SyncAssetV1TypeEnum {
AssetType toAssetType() => switch (this) {
SyncAssetV1TypeEnum.IMAGE => AssetType.image,
SyncAssetV1TypeEnum.VIDEO => AssetType.video,
SyncAssetV1TypeEnum.AUDIO => AssetType.audio,
SyncAssetV1TypeEnum.OTHER => AssetType.other,
_ => throw Exception('Unknown SyncAssetV1TypeEnum value: $this'),
};
}

View file

@ -6,7 +6,5 @@ mixin AssetEntityMixin on Table {
IntColumn get type => intEnum<AssetType>()();
DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
DateTimeColumn get updatedAt => dateTime().withDefault(currentDateAndTime)();
IntColumn get width => integer().nullable()();
IntColumn get height => integer().nullable()();
IntColumn get durationInSeconds => integer().nullable()();
}

View file

@ -8,6 +8,9 @@ class NullableValue<T> {
const NullableValue._(this._value, {bool present = false})
: _present = present;
/// Creates an instance without value
const NullableValue.absent() : this._(null, present: false);
/// Forces the value to be null
const NullableValue.empty() : this._(null, present: true);

View file

@ -361,7 +361,8 @@ void main() {
updateTimeCond: any(named: 'updateTimeCond'),
),
).thenAnswer((_) async => [newAsset]);
final dbAlbumNoThumb = dbAlbum.copyWith(thumbnailId: null);
final dbAlbumNoThumb =
dbAlbum.copyWith(thumbnailId: const NullableValue.absent());
final result =
await sut.updateAlbum(dbAlbumNoThumb, LocalAlbumStub.album1);
@ -612,7 +613,8 @@ void main() {
});
test('returns true and sets new thumbnail if db thumb is null', () async {
final dbAlbumNoThumb = dbAlbum.copyWith(thumbnailId: null);
final dbAlbumNoThumb =
dbAlbum.copyWith(thumbnailId: const NullableValue.empty());
final newAsset = LocalAssetStub.image2.copyWith(
localId: "asset2",
createdAt: DateTime(2024, 1, 1, 10, 30, 0),