2022-07-13 14:23:48 +02:00
|
|
|
import 'dart:async';
|
2022-02-03 18:06:44 +02:00
|
|
|
|
|
|
|
import 'package:flutter/material.dart';
|
2022-06-25 20:46:51 +02:00
|
|
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
2022-11-08 19:00:24 +02:00
|
|
|
import 'package:immich_mobile/shared/models/asset.dart';
|
2023-03-04 00:38:30 +02:00
|
|
|
import 'package:immich_mobile/shared/models/exif_info.dart';
|
2023-02-09 19:32:08 +02:00
|
|
|
import 'package:immich_mobile/shared/models/store.dart';
|
2022-08-03 07:04:34 +02:00
|
|
|
import 'package:immich_mobile/shared/providers/api.provider.dart';
|
2023-03-04 00:38:30 +02:00
|
|
|
import 'package:immich_mobile/shared/providers/db.provider.dart';
|
2022-07-13 14:23:48 +02:00
|
|
|
import 'package:immich_mobile/shared/services/api.service.dart';
|
2023-03-04 00:38:30 +02:00
|
|
|
import 'package:immich_mobile/shared/services/sync.service.dart';
|
2022-11-26 18:16:02 +02:00
|
|
|
import 'package:immich_mobile/utils/openapi_extensions.dart';
|
|
|
|
import 'package:immich_mobile/utils/tuple.dart';
|
2023-03-04 00:38:30 +02:00
|
|
|
import 'package:isar/isar.dart';
|
2022-11-30 18:58:07 +02:00
|
|
|
import 'package:logging/logging.dart';
|
2022-07-13 14:23:48 +02:00
|
|
|
import 'package:openapi/api.dart';
|
2022-02-03 18:06:44 +02:00
|
|
|
|
2022-07-13 14:23:48 +02:00
|
|
|
final assetServiceProvider = Provider(
|
|
|
|
(ref) => AssetService(
|
|
|
|
ref.watch(apiServiceProvider),
|
2023-03-04 00:38:30 +02:00
|
|
|
ref.watch(syncServiceProvider),
|
|
|
|
ref.watch(dbProvider),
|
2022-07-13 14:23:48 +02:00
|
|
|
),
|
|
|
|
);
|
2022-06-25 20:46:51 +02:00
|
|
|
|
2022-02-03 18:06:44 +02:00
|
|
|
class AssetService {
|
2022-07-13 14:23:48 +02:00
|
|
|
final ApiService _apiService;
|
2023-03-04 00:38:30 +02:00
|
|
|
final SyncService _syncService;
|
2022-11-30 18:58:07 +02:00
|
|
|
final log = Logger('AssetService');
|
2023-03-04 00:38:30 +02:00
|
|
|
final Isar _db;
|
2022-02-03 18:06:44 +02:00
|
|
|
|
2023-03-04 00:38:30 +02:00
|
|
|
AssetService(
|
|
|
|
this._apiService,
|
|
|
|
this._syncService,
|
|
|
|
this._db,
|
|
|
|
);
|
|
|
|
|
|
|
|
/// Checks the server for updated assets and updates the local database if
|
|
|
|
/// required. Returns `true` if there were any changes.
|
|
|
|
Future<bool> refreshRemoteAssets() async {
|
|
|
|
final Stopwatch sw = Stopwatch()..start();
|
|
|
|
final int numOwnedRemoteAssets = await _db.assets
|
|
|
|
.where()
|
|
|
|
.remoteIdIsNotNull()
|
|
|
|
.filter()
|
2023-03-23 03:36:44 +02:00
|
|
|
.ownerIdEqualTo(Store.get(StoreKey.currentUser).isarId)
|
2023-03-04 00:38:30 +02:00
|
|
|
.count();
|
2023-03-27 04:35:52 +02:00
|
|
|
final bool changes = await _syncService.syncRemoteAssetsToDb(
|
|
|
|
() async => (await _getRemoteAssets(hasCache: numOwnedRemoteAssets > 0))
|
|
|
|
?.map(Asset.remote)
|
|
|
|
.toList(),
|
|
|
|
);
|
2023-03-04 00:38:30 +02:00
|
|
|
debugPrint("refreshRemoteAssets full took ${sw.elapsedMilliseconds}ms");
|
|
|
|
return changes;
|
|
|
|
}
|
2022-02-07 04:31:32 +02:00
|
|
|
|
2022-11-26 18:16:02 +02:00
|
|
|
/// Returns `null` if the server state did not change, else list of assets
|
2023-03-04 00:38:30 +02:00
|
|
|
Future<List<AssetResponseDto>?> _getRemoteAssets({
|
|
|
|
required bool hasCache,
|
|
|
|
}) async {
|
2022-11-30 18:58:07 +02:00
|
|
|
try {
|
2023-03-23 03:36:44 +02:00
|
|
|
final etag = hasCache ? Store.tryGet(StoreKey.assetETag) : null;
|
2022-12-02 22:55:10 +02:00
|
|
|
final Pair<List<AssetResponseDto>, String?>? remote =
|
|
|
|
await _apiService.assetApi.getAllAssetsWithETag(eTag: etag);
|
2022-11-30 18:58:07 +02:00
|
|
|
if (remote == null) {
|
2023-03-04 00:38:30 +02:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
if (remote.second != null && remote.second != etag) {
|
|
|
|
Store.put(StoreKey.assetETag, remote.second);
|
2022-11-30 18:58:07 +02:00
|
|
|
}
|
2023-03-04 00:38:30 +02:00
|
|
|
return remote.first;
|
2022-11-30 18:58:07 +02:00
|
|
|
} catch (e, stack) {
|
|
|
|
log.severe('Error while getting remote assets', e, stack);
|
2023-03-04 00:38:30 +02:00
|
|
|
return null;
|
2022-02-13 23:10:42 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-13 14:23:48 +02:00
|
|
|
Future<List<DeleteAssetResponseDto>?> deleteAssets(
|
2023-02-04 22:42:42 +02:00
|
|
|
Iterable<Asset> deleteAssets,
|
2022-07-13 14:23:48 +02:00
|
|
|
) async {
|
2022-02-13 23:10:42 +02:00
|
|
|
try {
|
2022-11-08 19:00:24 +02:00
|
|
|
final List<String> payload = [];
|
2022-02-13 23:10:42 +02:00
|
|
|
|
2022-11-08 19:00:24 +02:00
|
|
|
for (final asset in deleteAssets) {
|
2023-02-04 22:42:42 +02:00
|
|
|
payload.add(asset.remoteId!);
|
2022-02-13 23:10:42 +02:00
|
|
|
}
|
|
|
|
|
2022-07-13 14:23:48 +02:00
|
|
|
return await _apiService.assetApi
|
|
|
|
.deleteAsset(DeleteAssetDto(ids: payload));
|
2022-02-11 04:40:11 +02:00
|
|
|
} catch (e) {
|
|
|
|
debugPrint("Error getAllAsset ${e.toString()}");
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
2023-02-05 05:25:11 +02:00
|
|
|
|
2023-03-04 00:38:30 +02:00
|
|
|
/// Loads the exif information from the database. If there is none, loads
|
|
|
|
/// the exif info from the server (remote assets only)
|
|
|
|
Future<Asset> loadExif(Asset a) async {
|
|
|
|
a.exifInfo ??= await _db.exifInfos.get(a.id);
|
|
|
|
if (a.exifInfo?.iso == null) {
|
|
|
|
if (a.isRemote) {
|
|
|
|
final dto = await _apiService.assetApi.getAssetById(a.remoteId!);
|
|
|
|
if (dto != null && dto.exifInfo != null) {
|
|
|
|
a = a.withUpdatesFromDto(dto);
|
|
|
|
if (a.isInDb) {
|
|
|
|
_db.writeTxn(() => a.put(_db));
|
|
|
|
} else {
|
|
|
|
debugPrint("[loadExif] parameter Asset is not from DB!");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// TODO implement local exif info parsing
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return a;
|
|
|
|
}
|
|
|
|
|
2023-02-09 19:32:08 +02:00
|
|
|
Future<Asset?> updateAsset(
|
|
|
|
Asset asset,
|
|
|
|
UpdateAssetDto updateAssetDto,
|
|
|
|
) async {
|
|
|
|
final dto =
|
|
|
|
await _apiService.assetApi.updateAsset(asset.remoteId!, updateAssetDto);
|
2023-04-17 07:02:07 +02:00
|
|
|
if (dto != null) {
|
|
|
|
final updated = Asset.remote(dto).updateFromDb(asset);
|
|
|
|
if (updated.isInDb) {
|
|
|
|
await _db.writeTxn(() => updated.put(_db));
|
|
|
|
}
|
|
|
|
return updated;
|
|
|
|
}
|
|
|
|
return null;
|
2023-02-05 05:25:11 +02:00
|
|
|
}
|
|
|
|
|
2023-02-09 19:32:08 +02:00
|
|
|
Future<Asset?> changeFavoriteStatus(Asset asset, bool isFavorite) {
|
2023-02-05 05:25:11 +02:00
|
|
|
return updateAsset(asset, UpdateAssetDto(isFavorite: isFavorite));
|
|
|
|
}
|
2023-04-17 07:02:07 +02:00
|
|
|
|
|
|
|
Future<Asset?> changeArchiveStatus(Asset asset, bool isArchive) {
|
|
|
|
return updateAsset(asset, UpdateAssetDto(isArchived: isArchive));
|
|
|
|
}
|
2022-02-03 18:06:44 +02:00
|
|
|
}
|