mirror of
https://github.com/immich-app/immich.git
synced 2025-12-17 01:11:13 +03:00
chore: bump line length to 120 (#20191)
This commit is contained in:
@@ -192,8 +192,7 @@ class ActionService {
|
||||
final result = await _albumApiRepository.removeAssets(albumId, remoteIds);
|
||||
|
||||
if (result.removed.isNotEmpty) {
|
||||
removedCount =
|
||||
await _remoteAlbumRepository.removeAssets(albumId, result.removed);
|
||||
removedCount = await _remoteAlbumRepository.removeAssets(albumId, result.removed);
|
||||
}
|
||||
|
||||
return removedCount;
|
||||
|
||||
@@ -11,8 +11,7 @@ import 'package:immich_mobile/domain/services/user.service.dart';
|
||||
import 'package:immich_mobile/entities/album.entity.dart';
|
||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||
import 'package:immich_mobile/entities/backup_album.entity.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/user.entity.dart'
|
||||
as entity;
|
||||
import 'package:immich_mobile/infrastructure/entities/user.entity.dart' as entity;
|
||||
import 'package:immich_mobile/models/albums/album_add_asset_response.model.dart';
|
||||
import 'package:immich_mobile/models/albums/album_search.model.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/user.provider.dart';
|
||||
@@ -76,12 +75,8 @@ class AlbumService {
|
||||
bool changes = false;
|
||||
try {
|
||||
final (selectedIds, excludedIds, onDevice) = await (
|
||||
_backupAlbumRepository
|
||||
.getIdsBySelection(BackupSelection.select)
|
||||
.then((value) => value.toSet()),
|
||||
_backupAlbumRepository
|
||||
.getIdsBySelection(BackupSelection.exclude)
|
||||
.then((value) => value.toSet()),
|
||||
_backupAlbumRepository.getIdsBySelection(BackupSelection.select).then((value) => value.toSet()),
|
||||
_backupAlbumRepository.getIdsBySelection(BackupSelection.exclude).then((value) => value.toSet()),
|
||||
_albumMediaRepository.getAll()
|
||||
).wait;
|
||||
_log.info("Found ${onDevice.length} device albums");
|
||||
@@ -126,8 +121,7 @@ class AlbumService {
|
||||
onDevice.removeWhere((album) => !selectedIds.contains(album.localId));
|
||||
_log.info("'Recents' is not selected, keeping only selected albums");
|
||||
}
|
||||
changes =
|
||||
await _syncService.syncLocalAlbumAssetsToDb(onDevice, excludedAssets);
|
||||
changes = await _syncService.syncLocalAlbumAssetsToDb(onDevice, excludedAssets);
|
||||
_log.info("Syncing completed. Changes: $changes");
|
||||
} finally {
|
||||
_localCompleter.complete(changes);
|
||||
@@ -141,14 +135,10 @@ class AlbumService {
|
||||
Set<String> excludedAlbumIds,
|
||||
) async {
|
||||
final Set<String> result = HashSet<String>();
|
||||
for (final batchAlbums in albums
|
||||
.where((album) => excludedAlbumIds.contains(album.localId))
|
||||
.slices(5)) {
|
||||
for (final batchAlbums in albums.where((album) => excludedAlbumIds.contains(album.localId)).slices(5)) {
|
||||
await batchAlbums
|
||||
.map(
|
||||
(album) => _albumMediaRepository
|
||||
.getAssetIds(album.localId!)
|
||||
.then((assetIds) => result.addAll(assetIds)),
|
||||
(album) => _albumMediaRepository.getAssetIds(album.localId!).then((assetIds) => result.addAll(assetIds)),
|
||||
)
|
||||
.wait;
|
||||
}
|
||||
@@ -244,9 +234,8 @@ class AlbumService {
|
||||
assets.map((asset) => asset.remoteId!),
|
||||
);
|
||||
|
||||
final List<Asset> addedAssets = result.added
|
||||
.map((id) => assets.firstWhere((asset) => asset.remoteId == id))
|
||||
.toList();
|
||||
final List<Asset> addedAssets =
|
||||
result.added.map((id) => assets.firstWhere((asset) => asset.remoteId == id)).toList();
|
||||
|
||||
await _updateAssets(album.id, add: addedAssets);
|
||||
|
||||
@@ -296,8 +285,7 @@ class AlbumService {
|
||||
await _albumApiRepository.delete(album.remoteId!);
|
||||
}
|
||||
if (album.shared) {
|
||||
final foreignAssets =
|
||||
await _assetRepository.getByAlbum(album, notOwnedBy: [userId]);
|
||||
final foreignAssets = await _assetRepository.getByAlbum(album, notOwnedBy: [userId]);
|
||||
await _albumRepository.delete(album.id);
|
||||
|
||||
final List<Album> albums = await _albumRepository.getAll(shared: true);
|
||||
@@ -307,8 +295,7 @@ class AlbumService {
|
||||
await _assetRepository.getByAlbum(album, notOwnedBy: [userId]),
|
||||
);
|
||||
}
|
||||
final List<int> idsToRemove =
|
||||
_syncService.sharedAssetsToRemove(foreignAssets, existing);
|
||||
final List<int> idsToRemove = _syncService.sharedAssetsToRemove(foreignAssets, existing);
|
||||
if (idsToRemove.isNotEmpty) {
|
||||
await _assetRepository.deleteByIds(idsToRemove);
|
||||
}
|
||||
@@ -341,8 +328,7 @@ class AlbumService {
|
||||
album.remoteId!,
|
||||
assets.map((asset) => asset.remoteId!),
|
||||
);
|
||||
final toRemove = result.removed
|
||||
.map((id) => assets.firstWhere((asset) => asset.remoteId == id));
|
||||
final toRemove = result.removed.map((id) => assets.firstWhere((asset) => asset.remoteId == id));
|
||||
await _updateAssets(album.id, remove: toRemove.toList());
|
||||
return true;
|
||||
} catch (e) {
|
||||
@@ -379,8 +365,7 @@ class AlbumService {
|
||||
List<String> userIds,
|
||||
) async {
|
||||
try {
|
||||
final updatedAlbum =
|
||||
await _albumApiRepository.addUsers(album.remoteId!, userIds);
|
||||
final updatedAlbum = await _albumApiRepository.addUsers(album.remoteId!, userIds);
|
||||
|
||||
album.sharedUsers.addAll(updatedAlbum.remoteUsers);
|
||||
album.shared = true;
|
||||
@@ -503,8 +488,7 @@ class AlbumService {
|
||||
|
||||
Future<Album?> updateSortOrder(Album album, SortOrder order) async {
|
||||
try {
|
||||
final updateAlbum =
|
||||
await _albumApiRepository.update(album.remoteId!, sortOrder: order);
|
||||
final updateAlbum = await _albumApiRepository.update(album.remoteId!, sortOrder: order);
|
||||
album.sortOrder = updateAlbum.sortOrder;
|
||||
|
||||
return _albumRepository.update(album);
|
||||
|
||||
@@ -178,13 +178,11 @@ class ApiService implements Authentication {
|
||||
|
||||
if (Platform.isIOS) {
|
||||
final iosInfo = await deviceInfoPlugin.iosInfo;
|
||||
authenticationApi.apiClient
|
||||
.addDefaultHeader('deviceModel', iosInfo.utsname.machine);
|
||||
authenticationApi.apiClient.addDefaultHeader('deviceModel', iosInfo.utsname.machine);
|
||||
authenticationApi.apiClient.addDefaultHeader('deviceType', 'iOS');
|
||||
} else if (Platform.isAndroid) {
|
||||
final androidInfo = await deviceInfoPlugin.androidInfo;
|
||||
authenticationApi.apiClient
|
||||
.addDefaultHeader('deviceModel', androidInfo.model);
|
||||
authenticationApi.apiClient.addDefaultHeader('deviceModel', androidInfo.model);
|
||||
authenticationApi.apiClient.addDefaultHeader('deviceType', 'Android');
|
||||
} else {
|
||||
authenticationApi.apiClient.addDefaultHeader('deviceModel', 'Unknown');
|
||||
|
||||
@@ -78,11 +78,8 @@ class AssetService {
|
||||
/// required. Returns `true` if there were any changes.
|
||||
Future<bool> refreshRemoteAssets() async {
|
||||
final syncedUserIds = await _etagRepository.getAllIds();
|
||||
final List<UserDto> syncedUsers = syncedUserIds.isEmpty
|
||||
? []
|
||||
: (await _isarUserRepository.getByUserIds(syncedUserIds))
|
||||
.nonNulls
|
||||
.toList();
|
||||
final List<UserDto> syncedUsers =
|
||||
syncedUserIds.isEmpty ? [] : (await _isarUserRepository.getByUserIds(syncedUserIds)).nonNulls.toList();
|
||||
final Stopwatch sw = Stopwatch()..start();
|
||||
final bool changes = await _syncService.syncRemoteAssetsToDb(
|
||||
users: syncedUsers,
|
||||
@@ -94,8 +91,10 @@ class AssetService {
|
||||
}
|
||||
|
||||
/// Returns `(null, null)` if changes are invalid -> requires full sync
|
||||
Future<(List<Asset>? toUpsert, List<String>? toDelete)>
|
||||
_getRemoteAssetChanges(List<UserDto> users, DateTime since) async {
|
||||
Future<(List<Asset>? toUpsert, List<String>? toDelete)> _getRemoteAssetChanges(
|
||||
List<UserDto> users,
|
||||
DateTime since,
|
||||
) async {
|
||||
final dto = AssetDeltaSyncDto(
|
||||
updatedAfter: since,
|
||||
userIds: users.map((e) => e.id).toList(),
|
||||
@@ -112,8 +111,7 @@ class AssetService {
|
||||
String remoteId,
|
||||
) async {
|
||||
try {
|
||||
final AssetResponseDto? dto =
|
||||
await _apiService.assetsApi.getAssetInfo(remoteId);
|
||||
final AssetResponseDto? dto = await _apiService.assetsApi.getAssetInfo(remoteId);
|
||||
|
||||
return dto?.people;
|
||||
} catch (error, stack) {
|
||||
@@ -142,8 +140,7 @@ class AssetService {
|
||||
userId: user.id,
|
||||
);
|
||||
log.fine("Requesting $chunkSize assets from $lastId");
|
||||
final List<AssetResponseDto>? assets =
|
||||
await _apiService.syncApi.getFullSyncForUser(dto);
|
||||
final List<AssetResponseDto>? assets = await _apiService.syncApi.getFullSyncForUser(dto);
|
||||
if (assets == null) return null;
|
||||
log.fine(
|
||||
"Received ${assets.length} assets from ${assets.firstOrNull?.id} to ${assets.lastOrNull?.id}",
|
||||
@@ -172,8 +169,7 @@ class AssetService {
|
||||
a.exifInfo = newExif;
|
||||
if (newExif != a.exifInfo) {
|
||||
if (a.isInDb) {
|
||||
await _assetRepository
|
||||
.transaction(() => _assetRepository.update(a));
|
||||
await _assetRepository.transaction(() => _assetRepository.update(a));
|
||||
} else {
|
||||
debugPrint("[loadExif] parameter Asset is not from DB!");
|
||||
}
|
||||
@@ -230,16 +226,13 @@ class AssetService {
|
||||
await updateAssets(
|
||||
assets,
|
||||
UpdateAssetDto(
|
||||
visibility:
|
||||
isArchived ? AssetVisibility.archive : AssetVisibility.timeline,
|
||||
visibility: isArchived ? AssetVisibility.archive : AssetVisibility.timeline,
|
||||
),
|
||||
);
|
||||
|
||||
for (var element in assets) {
|
||||
element.isArchived = isArchived;
|
||||
element.visibility = isArchived
|
||||
? AssetVisibilityEnum.archive
|
||||
: AssetVisibilityEnum.timeline;
|
||||
element.visibility = isArchived ? AssetVisibilityEnum.archive : AssetVisibilityEnum.timeline;
|
||||
}
|
||||
|
||||
await _syncService.upsertAssetsWithExif(assets);
|
||||
@@ -263,8 +256,7 @@ class AssetService {
|
||||
|
||||
for (var element in assets) {
|
||||
element.fileCreatedAt = DateTime.parse(updatedDt);
|
||||
element.exifInfo = element.exifInfo
|
||||
?.copyWith(dateTimeOriginal: DateTime.parse(updatedDt));
|
||||
element.exifInfo = element.exifInfo?.copyWith(dateTimeOriginal: DateTime.parse(updatedDt));
|
||||
}
|
||||
|
||||
await _syncService.upsertAssetsWithExif(assets);
|
||||
@@ -307,10 +299,8 @@ class AssetService {
|
||||
|
||||
Future<void> syncUploadedAssetToAlbums() async {
|
||||
try {
|
||||
final selectedAlbums =
|
||||
await _backupRepository.getAllBySelection(BackupSelection.select);
|
||||
final excludedAlbums =
|
||||
await _backupRepository.getAllBySelection(BackupSelection.exclude);
|
||||
final selectedAlbums = await _backupRepository.getAllBySelection(BackupSelection.select);
|
||||
final excludedAlbums = await _backupRepository.getAllBySelection(BackupSelection.exclude);
|
||||
|
||||
final candidates = await _backupService.buildUploadCandidates(
|
||||
selectedAlbums,
|
||||
@@ -375,8 +365,7 @@ class AssetService {
|
||||
var exifInfo = await _exifInfoRepository.get(localExifId);
|
||||
|
||||
if (exifInfo != null) {
|
||||
await _exifInfoRepository
|
||||
.update(exifInfo.copyWith(description: description));
|
||||
await _exifInfoRepository.update(exifInfo.copyWith(description: description));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -429,22 +418,16 @@ class AssetService {
|
||||
// Delete files from local gallery
|
||||
final candidates = assets.where((asset) => asset.isLocal);
|
||||
|
||||
final deletedIds = await _assetMediaRepository
|
||||
.deleteAll(candidates.map((asset) => asset.localId!).toList());
|
||||
final deletedIds = await _assetMediaRepository.deleteAll(candidates.map((asset) => asset.localId!).toList());
|
||||
|
||||
// Modify local database by removing the reference to the local assets
|
||||
if (deletedIds.isNotEmpty) {
|
||||
// Delete records from local database
|
||||
final isarIds = assets
|
||||
.where((asset) => asset.storage == AssetState.local)
|
||||
.map((asset) => asset.id)
|
||||
.toList();
|
||||
final isarIds = assets.where((asset) => asset.storage == AssetState.local).map((asset) => asset.id).toList();
|
||||
await _assetRepository.deleteByIds(isarIds);
|
||||
|
||||
// Modify Merged asset to be remote only
|
||||
final updatedAssets = assets
|
||||
.where((asset) => asset.storage == AssetState.merged)
|
||||
.map((asset) {
|
||||
final updatedAssets = assets.where((asset) => asset.storage == AssetState.merged).map((asset) {
|
||||
asset.localId = null;
|
||||
return asset;
|
||||
}).toList();
|
||||
@@ -473,9 +456,7 @@ class AssetService {
|
||||
|
||||
/// Update asset info bassed on the deletion type.
|
||||
final payload = shouldDeletePermanently
|
||||
? assets
|
||||
.where((asset) => asset.storage == AssetState.merged)
|
||||
.map((asset) {
|
||||
? assets.where((asset) => asset.storage == AssetState.merged).map((asset) {
|
||||
asset.remoteId = null;
|
||||
asset.visibility = AssetVisibilityEnum.timeline;
|
||||
return asset;
|
||||
@@ -489,10 +470,8 @@ class AssetService {
|
||||
await _assetRepository.updateAll(payload.toList());
|
||||
|
||||
if (shouldDeletePermanently) {
|
||||
final remoteAssetIds = assets
|
||||
.where((asset) => asset.storage == AssetState.remote)
|
||||
.map((asset) => asset.id)
|
||||
.toList();
|
||||
final remoteAssetIds =
|
||||
assets.where((asset) => asset.storage == AssetState.remote).map((asset) => asset.id).toList();
|
||||
await _assetRepository.deleteByIds(remoteAssetIds);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -39,10 +39,8 @@ final backgroundServiceProvider = Provider((ref) => BackgroundService());
|
||||
/// Background backup service
|
||||
class BackgroundService {
|
||||
static const String _portNameLock = "immichLock";
|
||||
static const MethodChannel _foregroundChannel =
|
||||
MethodChannel('immich/foregroundChannel');
|
||||
static const MethodChannel _backgroundChannel =
|
||||
MethodChannel('immich/backgroundChannel');
|
||||
static const MethodChannel _foregroundChannel = MethodChannel('immich/foregroundChannel');
|
||||
static const MethodChannel _backgroundChannel = MethodChannel('immich/backgroundChannel');
|
||||
static const notifyInterval = Duration(milliseconds: 400);
|
||||
bool _isBackgroundInitialized = false;
|
||||
CancellationToken? _cancellationToken;
|
||||
@@ -56,8 +54,7 @@ class BackgroundService {
|
||||
int _assetsToUploadCount = 0;
|
||||
String _lastPrintedDetailContent = "";
|
||||
String? _lastPrintedDetailTitle;
|
||||
late final ThrottleProgressUpdate _throttledNotifiy =
|
||||
ThrottleProgressUpdate(_updateProgress, notifyInterval);
|
||||
late final ThrottleProgressUpdate _throttledNotifiy = ThrottleProgressUpdate(_updateProgress, notifyInterval);
|
||||
late final ThrottleProgressUpdate _throttledDetailNotify =
|
||||
ThrottleProgressUpdate(_updateDetailProgress, notifyInterval);
|
||||
|
||||
@@ -74,10 +71,8 @@ class BackgroundService {
|
||||
Future<bool> enableService({bool immediate = false}) async {
|
||||
try {
|
||||
final callback = PluginUtilities.getCallbackHandle(_nativeEntry)!;
|
||||
final String title =
|
||||
"backup_background_service_default_notification".tr();
|
||||
final bool ok = await _foregroundChannel
|
||||
.invokeMethod('enable', [callback.toRawHandle(), title, immediate]);
|
||||
final String title = "backup_background_service_default_notification".tr();
|
||||
final bool ok = await _foregroundChannel.invokeMethod('enable', [callback.toRawHandle(), title, immediate]);
|
||||
return ok;
|
||||
} catch (error) {
|
||||
return false;
|
||||
@@ -133,8 +128,7 @@ class BackgroundService {
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
return await _foregroundChannel
|
||||
.invokeMethod('isIgnoringBatteryOptimizations');
|
||||
return await _foregroundChannel.invokeMethod('isIgnoringBatteryOptimizations');
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
@@ -183,8 +177,7 @@ class BackgroundService {
|
||||
}) async {
|
||||
try {
|
||||
if (_isBackgroundInitialized && _errorGracePeriodExceeded) {
|
||||
return await _backgroundChannel
|
||||
.invokeMethod('showError', [title, content, individualTag]);
|
||||
return await _backgroundChannel.invokeMethod('showError', [title, content, individualTag]);
|
||||
}
|
||||
} catch (error) {
|
||||
debugPrint("[_showErrorNotification] failed to communicate with plugin");
|
||||
@@ -240,8 +233,7 @@ class BackgroundService {
|
||||
final bs = tempRp.asBroadcastStream();
|
||||
while (_wantsLockTime == lockTime) {
|
||||
other.send(tempSp);
|
||||
final dynamic answer = await bs.first
|
||||
.timeout(const Duration(seconds: 3), onTimeout: () => null);
|
||||
final dynamic answer = await bs.first.timeout(const Duration(seconds: 3), onTimeout: () => null);
|
||||
if (_wantsLockTime != lockTime) {
|
||||
break;
|
||||
}
|
||||
@@ -257,8 +249,7 @@ class BackgroundService {
|
||||
} else if (answer == false) {
|
||||
// other isolate is still active
|
||||
}
|
||||
final dynamic isFinished = await bs.first
|
||||
.timeout(const Duration(seconds: 3), onTimeout: () => false);
|
||||
final dynamic isFinished = await bs.first.timeout(const Duration(seconds: 3), onTimeout: () => false);
|
||||
if (isFinished == true) {
|
||||
break;
|
||||
}
|
||||
@@ -358,9 +349,7 @@ class BackgroundService {
|
||||
);
|
||||
|
||||
HttpSSLOptions.apply();
|
||||
ref
|
||||
.read(apiServiceProvider)
|
||||
.setAccessToken(Store.get(StoreKey.accessToken));
|
||||
ref.read(apiServiceProvider).setAccessToken(Store.get(StoreKey.accessToken));
|
||||
await ref.read(authServiceProvider).setOpenApiServiceEndpoint();
|
||||
if (kDebugMode) {
|
||||
debugPrint(
|
||||
@@ -368,12 +357,8 @@ class BackgroundService {
|
||||
);
|
||||
}
|
||||
|
||||
final selectedAlbums = await ref
|
||||
.read(backupAlbumRepositoryProvider)
|
||||
.getAllBySelection(BackupSelection.select);
|
||||
final excludedAlbums = await ref
|
||||
.read(backupAlbumRepositoryProvider)
|
||||
.getAllBySelection(BackupSelection.exclude);
|
||||
final selectedAlbums = await ref.read(backupAlbumRepositoryProvider).getAllBySelection(BackupSelection.select);
|
||||
final excludedAlbums = await ref.read(backupAlbumRepositoryProvider).getAllBySelection(BackupSelection.exclude);
|
||||
if (selectedAlbums.isEmpty) {
|
||||
return true;
|
||||
}
|
||||
@@ -392,9 +377,7 @@ class BackgroundService {
|
||||
final backupAlbums = [...selectedAlbums, ...excludedAlbums];
|
||||
backupAlbums.sortBy((e) => e.id);
|
||||
|
||||
final dbAlbums = await ref
|
||||
.read(backupAlbumRepositoryProvider)
|
||||
.getAll(sort: BackupAlbumSort.id);
|
||||
final dbAlbums = await ref.read(backupAlbumRepositoryProvider).getAll(sort: BackupAlbumSort.id);
|
||||
final List<int> toDelete = [];
|
||||
final List<BackupAlbum> toUpsert = [];
|
||||
// stores the most recent `lastBackup` per album but always keeps the `selection` from the most recent DB state
|
||||
@@ -403,9 +386,7 @@ class BackgroundService {
|
||||
backupAlbums,
|
||||
compare: (BackupAlbum a, BackupAlbum b) => a.id.compareTo(b.id),
|
||||
both: (BackupAlbum a, BackupAlbum b) {
|
||||
a.lastBackup = a.lastBackup.isAfter(b.lastBackup)
|
||||
? a.lastBackup
|
||||
: b.lastBackup;
|
||||
a.lastBackup = a.lastBackup.isAfter(b.lastBackup) ? a.lastBackup : b.lastBackup;
|
||||
toUpsert.add(a);
|
||||
return true;
|
||||
},
|
||||
@@ -419,9 +400,7 @@ class BackgroundService {
|
||||
return false;
|
||||
}
|
||||
// Android should check for new assets added while performing backup
|
||||
} while (Platform.isAndroid &&
|
||||
true ==
|
||||
await _backgroundChannel.invokeMethod<bool>("hasContentChanged"));
|
||||
} while (Platform.isAndroid && true == await _backgroundChannel.invokeMethod<bool>("hasContentChanged"));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -432,10 +411,8 @@ class BackgroundService {
|
||||
List<BackupAlbum> excludedAlbums,
|
||||
) async {
|
||||
_errorGracePeriodExceeded = _isErrorGracePeriodExceeded(settingsService);
|
||||
final bool notifyTotalProgress = settingsService
|
||||
.getSetting<bool>(AppSettingsEnum.backgroundBackupTotalProgress);
|
||||
final bool notifySingleProgress = settingsService
|
||||
.getSetting<bool>(AppSettingsEnum.backgroundBackupSingleProgress);
|
||||
final bool notifyTotalProgress = settingsService.getSetting<bool>(AppSettingsEnum.backgroundBackupTotalProgress);
|
||||
final bool notifySingleProgress = settingsService.getSetting<bool>(AppSettingsEnum.backgroundBackupSingleProgress);
|
||||
|
||||
if (_canceledBySystem) {
|
||||
return false;
|
||||
@@ -489,10 +466,8 @@ class BackgroundService {
|
||||
onSuccess: (result) => _onAssetUploaded(
|
||||
shouldNotify: notifyTotalProgress,
|
||||
),
|
||||
onProgress: (bytes, totalBytes) =>
|
||||
_onProgress(bytes, totalBytes, shouldNotify: notifySingleProgress),
|
||||
onCurrentAsset: (asset) =>
|
||||
_onSetCurrentBackupAsset(asset, shouldNotify: notifySingleProgress),
|
||||
onProgress: (bytes, totalBytes) => _onProgress(bytes, totalBytes, shouldNotify: notifySingleProgress),
|
||||
onCurrentAsset: (asset) => _onSetCurrentBackupAsset(asset, shouldNotify: notifySingleProgress),
|
||||
onError: _onBackupError,
|
||||
isBackground: true,
|
||||
);
|
||||
@@ -527,8 +502,7 @@ class BackgroundService {
|
||||
}
|
||||
|
||||
void _updateDetailProgress(String? title, int progress, int total) {
|
||||
final String msg =
|
||||
total > 0 ? humanReadableBytesProgress(progress, total) : "";
|
||||
final String msg = total > 0 ? humanReadableBytesProgress(progress, total) : "";
|
||||
// only update if message actually differs (to stop many useless notification updates on large assets or slow connections)
|
||||
if (msg != _lastPrintedDetailContent || _lastPrintedDetailTitle != title) {
|
||||
_lastPrintedDetailContent = msg;
|
||||
@@ -557,8 +531,8 @@ class BackgroundService {
|
||||
|
||||
void _onBackupError(ErrorUploadAsset errorAssetInfo) {
|
||||
_showErrorNotification(
|
||||
title: "backup_background_service_upload_failure_notification"
|
||||
.tr(namedArgs: {'filename': errorAssetInfo.fileName}),
|
||||
title:
|
||||
"backup_background_service_upload_failure_notification".tr(namedArgs: {'filename': errorAssetInfo.fileName}),
|
||||
individualTag: errorAssetInfo.id,
|
||||
);
|
||||
}
|
||||
@@ -571,16 +545,14 @@ class BackgroundService {
|
||||
return;
|
||||
}
|
||||
|
||||
_throttledDetailNotify.title =
|
||||
"backup_background_service_current_upload_notification"
|
||||
.tr(namedArgs: {'filename': currentUploadAsset.fileName});
|
||||
_throttledDetailNotify.title = "backup_background_service_current_upload_notification"
|
||||
.tr(namedArgs: {'filename': currentUploadAsset.fileName});
|
||||
_throttledDetailNotify.progress = 0;
|
||||
_throttledDetailNotify.total = 0;
|
||||
}
|
||||
|
||||
bool _isErrorGracePeriodExceeded(AppSettingsService appSettingsService) {
|
||||
final int value = appSettingsService
|
||||
.getSetting(AppSettingsEnum.uploadErrorNotificationGracePeriod);
|
||||
final int value = appSettingsService.getSetting(AppSettingsEnum.uploadErrorNotificationGracePeriod);
|
||||
if (value == 0) {
|
||||
return true;
|
||||
} else if (value == 5) {
|
||||
|
||||
@@ -74,8 +74,7 @@ class BackupService {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _saveDuplicatedAssetIds(List<String> deviceAssetIds) =>
|
||||
_assetRepository.transaction(
|
||||
Future<void> _saveDuplicatedAssetIds(List<String> deviceAssetIds) => _assetRepository.transaction(
|
||||
() => _assetRepository.upsertDuplicatedAssets(deviceAssetIds),
|
||||
);
|
||||
|
||||
@@ -127,8 +126,7 @@ class BackupService {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (useTimeFilter &&
|
||||
localAlbum.modifiedAt.isBefore(backupAlbum.lastBackup)) {
|
||||
if (useTimeFilter && localAlbum.modifiedAt.isBefore(backupAlbum.lastBackup)) {
|
||||
continue;
|
||||
}
|
||||
final List<Asset> assets;
|
||||
@@ -189,8 +187,7 @@ class BackupService {
|
||||
final Set<String> existing = {};
|
||||
try {
|
||||
final String deviceId = Store.get(StoreKey.deviceId);
|
||||
final CheckExistingAssetsResponseDto? duplicates =
|
||||
await _apiService.assetsApi.checkExistingAssets(
|
||||
final CheckExistingAssetsResponseDto? duplicates = await _apiService.assetsApi.checkExistingAssets(
|
||||
CheckExistingAssetsDto(
|
||||
deviceAssetIds: candidates.map((c) => c.asset.localId!).toList(),
|
||||
deviceId: deviceId,
|
||||
@@ -215,8 +212,7 @@ class BackupService {
|
||||
}
|
||||
|
||||
Future<bool> _checkPermissions() async {
|
||||
if (Platform.isAndroid &&
|
||||
!(await pm.Permission.accessMediaLocation.status).isGranted) {
|
||||
if (Platform.isAndroid && !(await pm.Permission.accessMediaLocation.status).isGranted) {
|
||||
// double check that permission is granted here, to guard against
|
||||
// uploading corrupt assets without EXIF information
|
||||
_log.warning("Media location permission is not granted. "
|
||||
@@ -255,8 +251,7 @@ class BackupService {
|
||||
required void Function(CurrentUploadAsset asset) onCurrentAsset,
|
||||
required void Function(ErrorUploadAsset error) onError,
|
||||
}) async {
|
||||
final bool isIgnoreIcloudAssets =
|
||||
_appSetting.getSetting(AppSettingsEnum.ignoreIcloudAssets);
|
||||
final bool isIgnoreIcloudAssets = _appSetting.getSetting(AppSettingsEnum.ignoreIcloudAssets);
|
||||
final shouldSyncAlbums = _appSetting.getSetting(AppSettingsEnum.syncAlbums);
|
||||
final String deviceId = Store.get(StoreKey.deviceId);
|
||||
final String savedEndpoint = Store.get(StoreKey.serverEndpoint);
|
||||
@@ -279,8 +274,7 @@ class BackupService {
|
||||
File? livePhotoFile;
|
||||
|
||||
try {
|
||||
final isAvailableLocally =
|
||||
await asset.local!.isLocallyAvailable(isOrigin: true);
|
||||
final isAvailableLocally = await asset.local!.isLocallyAvailable(isOrigin: true);
|
||||
|
||||
// Handle getting files from iCloud
|
||||
if (!isAvailableLocally && Platform.isIOS) {
|
||||
@@ -292,17 +286,14 @@ class BackupService {
|
||||
onCurrentAsset(
|
||||
CurrentUploadAsset(
|
||||
id: asset.localId!,
|
||||
fileCreatedAt: asset.fileCreatedAt.year == 1970
|
||||
? asset.fileModifiedAt
|
||||
: asset.fileCreatedAt,
|
||||
fileCreatedAt: asset.fileCreatedAt.year == 1970 ? asset.fileModifiedAt : asset.fileCreatedAt,
|
||||
fileName: asset.fileName,
|
||||
fileType: _getAssetType(asset.type),
|
||||
iCloudAsset: true,
|
||||
),
|
||||
);
|
||||
|
||||
file =
|
||||
await asset.local!.loadFile(progressHandler: pmProgressHandler);
|
||||
file = await asset.local!.loadFile(progressHandler: pmProgressHandler);
|
||||
if (asset.local!.isLivePhoto) {
|
||||
livePhotoFile = await asset.local!.loadFile(
|
||||
withSubtype: true,
|
||||
@@ -310,18 +301,15 @@ class BackupService {
|
||||
);
|
||||
}
|
||||
} else {
|
||||
file =
|
||||
await asset.local!.originFile.timeout(const Duration(seconds: 5));
|
||||
file = await asset.local!.originFile.timeout(const Duration(seconds: 5));
|
||||
|
||||
if (asset.local!.isLivePhoto) {
|
||||
livePhotoFile = await asset.local!.originFileWithSubtype
|
||||
.timeout(const Duration(seconds: 5));
|
||||
livePhotoFile = await asset.local!.originFileWithSubtype.timeout(const Duration(seconds: 5));
|
||||
}
|
||||
}
|
||||
|
||||
if (file != null) {
|
||||
String? originalFileName =
|
||||
await _assetMediaRepository.getOriginalFilename(asset.localId!);
|
||||
String? originalFileName = await _assetMediaRepository.getOriginalFilename(asset.localId!);
|
||||
originalFileName ??= asset.fileName;
|
||||
|
||||
if (asset.local!.isLivePhoto) {
|
||||
@@ -349,10 +337,8 @@ class BackupService {
|
||||
baseRequest.headers.addAll(ApiService.getRequestHeaders());
|
||||
baseRequest.fields['deviceAssetId'] = asset.localId!;
|
||||
baseRequest.fields['deviceId'] = deviceId;
|
||||
baseRequest.fields['fileCreatedAt'] =
|
||||
asset.fileCreatedAt.toUtc().toIso8601String();
|
||||
baseRequest.fields['fileModifiedAt'] =
|
||||
asset.fileModifiedAt.toUtc().toIso8601String();
|
||||
baseRequest.fields['fileCreatedAt'] = asset.fileCreatedAt.toUtc().toIso8601String();
|
||||
baseRequest.fields['fileModifiedAt'] = asset.fileModifiedAt.toUtc().toIso8601String();
|
||||
baseRequest.fields['isFavorite'] = asset.isFavorite.toString();
|
||||
baseRequest.fields['duration'] = asset.duration.toString();
|
||||
baseRequest.files.add(assetRawUploadData);
|
||||
@@ -360,9 +346,7 @@ class BackupService {
|
||||
onCurrentAsset(
|
||||
CurrentUploadAsset(
|
||||
id: asset.localId!,
|
||||
fileCreatedAt: asset.fileCreatedAt.year == 1970
|
||||
? asset.fileModifiedAt
|
||||
: asset.fileCreatedAt,
|
||||
fileCreatedAt: asset.fileCreatedAt.year == 1970 ? asset.fileModifiedAt : asset.fileCreatedAt,
|
||||
fileName: originalFileName,
|
||||
fileType: _getAssetType(asset.type),
|
||||
fileSize: file.lengthSync(),
|
||||
@@ -389,8 +373,7 @@ class BackupService {
|
||||
cancellationToken: cancelToken,
|
||||
);
|
||||
|
||||
final responseBody =
|
||||
jsonDecode(await response.stream.bytesToString());
|
||||
final responseBody = jsonDecode(await response.stream.bytesToString());
|
||||
|
||||
if (![200, 201].contains(response.statusCode)) {
|
||||
final error = responseBody;
|
||||
|
||||
@@ -181,10 +181,8 @@ class BackupVerificationService {
|
||||
|
||||
// for images: make sure they are pixel-wise identical
|
||||
// (skip first few KBs containing metadata)
|
||||
final Uint64List localImage =
|
||||
_fakeDecodeImg(await file.readAsBytes());
|
||||
final res = await apiService.assetsApi
|
||||
.downloadAssetWithHttpInfo(remote.remoteId!);
|
||||
final Uint64List localImage = _fakeDecodeImg(await file.readAsBytes());
|
||||
final res = await apiService.assetsApi.downloadAssetWithHttpInfo(remote.remoteId!);
|
||||
final Uint64List remoteImage = _fakeDecodeImg(res.bodyBytes);
|
||||
|
||||
final eq = const ListEquality().equals(remoteImage, localImage);
|
||||
@@ -198,9 +196,7 @@ class BackupVerificationService {
|
||||
|
||||
static Uint64List _fakeDecodeImg(Uint8List bytes) {
|
||||
const headerLength = 131072; // assume header is at most 128 KB
|
||||
final start = bytes.length < headerLength * 2
|
||||
? (bytes.length ~/ (4 * 8)) * 8
|
||||
: headerLength;
|
||||
final start = bytes.length < headerLength * 2 ? (bytes.length ~/ (4 * 8)) * 8 : headerLength;
|
||||
return bytes.buffer.asUint64List(start);
|
||||
}
|
||||
|
||||
|
||||
@@ -67,8 +67,7 @@ class DeepLinkService {
|
||||
) async {
|
||||
final path = link.uri.path;
|
||||
|
||||
const uuidRegex =
|
||||
r'[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}';
|
||||
const uuidRegex = r'[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}';
|
||||
final assetRegex = RegExp('/photos/($uuidRegex)');
|
||||
final albumRegex = RegExp('/albums/($uuidRegex)');
|
||||
|
||||
|
||||
@@ -35,8 +35,7 @@ class DownloadService {
|
||||
) {
|
||||
_downloadRepository.onImageDownloadStatus = _onImageDownloadCallback;
|
||||
_downloadRepository.onVideoDownloadStatus = _onVideoDownloadCallback;
|
||||
_downloadRepository.onLivePhotoDownloadStatus =
|
||||
_onLivePhotoDownloadCallback;
|
||||
_downloadRepository.onLivePhotoDownloadStatus = _onLivePhotoDownloadCallback;
|
||||
_downloadRepository.onTaskProgress = _onTaskProgressCallback;
|
||||
}
|
||||
|
||||
@@ -108,10 +107,8 @@ class DownloadService {
|
||||
return false;
|
||||
}
|
||||
|
||||
final imageRecord =
|
||||
_findTaskRecord(records, livePhotosId, LivePhotosPart.image);
|
||||
final videoRecord =
|
||||
_findTaskRecord(records, livePhotosId, LivePhotosPart.video);
|
||||
final imageRecord = _findTaskRecord(records, livePhotosId, LivePhotosPart.image);
|
||||
final videoRecord = _findTaskRecord(records, livePhotosId, LivePhotosPart.video);
|
||||
final imageFilePath = await imageRecord.task.filePath();
|
||||
final videoFilePath = await videoRecord.task.filePath();
|
||||
|
||||
@@ -126,8 +123,7 @@ class DownloadService {
|
||||
} on PlatformException catch (error, stack) {
|
||||
// Handle saving MotionPhotos on iOS
|
||||
if (error.code == 'PHPhotosErrorDomain (-1)') {
|
||||
final result = await _fileMediaRepository
|
||||
.saveImageWithFile(imageFilePath, title: task.filename);
|
||||
final result = await _fileMediaRepository.saveImageWithFile(imageFilePath, title: task.filename);
|
||||
return result != null;
|
||||
}
|
||||
_log.severe("Error saving live photo", error, stack);
|
||||
@@ -158,8 +154,7 @@ class DownloadService {
|
||||
}
|
||||
|
||||
Future<List<bool>> downloadAll(List<Asset> assets) async {
|
||||
return await _downloadRepository
|
||||
.downloadAll(assets.expand(_createDownloadTasks).toList());
|
||||
return await _downloadRepository.downloadAll(assets.expand(_createDownloadTasks).toList());
|
||||
}
|
||||
|
||||
Future<void> download(Asset asset) async {
|
||||
@@ -181,9 +176,7 @@ class DownloadService {
|
||||
),
|
||||
_buildDownloadTask(
|
||||
asset.livePhotoVideoId!,
|
||||
asset.fileName
|
||||
.toUpperCase()
|
||||
.replaceAll(RegExp(r"\.(JPG|HEIC)$"), '.MOV'),
|
||||
asset.fileName.toUpperCase().replaceAll(RegExp(r"\.(JPG|HEIC)$"), '.MOV'),
|
||||
group: kDownloadGroupLivePhoto,
|
||||
metadata: LivePhotosMetadata(
|
||||
part: LivePhotosPart.video,
|
||||
|
||||
@@ -144,8 +144,7 @@ class DriftBackupService {
|
||||
}
|
||||
final response = jsonDecode(update.responseBody!);
|
||||
|
||||
final localAsset =
|
||||
await _localAssetRepository.getById(metadata.localAssetId);
|
||||
final localAsset = await _localAssetRepository.getById(metadata.localAssetId);
|
||||
if (localAsset == null) {
|
||||
return;
|
||||
}
|
||||
@@ -313,6 +312,5 @@ class UploadTaskMetadata {
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
localAssetId.hashCode ^ isLivePhotos.hashCode ^ livePhotoVideoId.hashCode;
|
||||
int get hashCode => localAssetId.hashCode ^ isLivePhotos.hashCode ^ livePhotoVideoId.hashCode;
|
||||
}
|
||||
|
||||
@@ -20,25 +20,21 @@ class EntityService {
|
||||
final user = await _isarUserRepository.getByUserId(ownerId);
|
||||
album.owner.value = user == null ? null : User.fromDto(user);
|
||||
}
|
||||
final thumbnailAssetId =
|
||||
album.remoteThumbnailAssetId ?? album.thumbnail.value?.remoteId;
|
||||
final thumbnailAssetId = album.remoteThumbnailAssetId ?? album.thumbnail.value?.remoteId;
|
||||
if (thumbnailAssetId != null) {
|
||||
// set thumbnail with asset from database
|
||||
album.thumbnail.value =
|
||||
await _assetRepository.getByRemoteId(thumbnailAssetId);
|
||||
album.thumbnail.value = await _assetRepository.getByRemoteId(thumbnailAssetId);
|
||||
}
|
||||
if (album.remoteUsers.isNotEmpty) {
|
||||
// replace all users with users from database
|
||||
final users = await _isarUserRepository
|
||||
.getByUserIds(album.remoteUsers.map((user) => user.id).toList());
|
||||
final users = await _isarUserRepository.getByUserIds(album.remoteUsers.map((user) => user.id).toList());
|
||||
album.sharedUsers.clear();
|
||||
album.sharedUsers.addAll(users.nonNulls.map(User.fromDto));
|
||||
album.shared = true;
|
||||
}
|
||||
if (album.remoteAssets.isNotEmpty) {
|
||||
// replace all assets with assets from database
|
||||
final assets = await _assetRepository
|
||||
.getAllByRemoteId(album.remoteAssets.map((asset) => asset.remoteId!));
|
||||
final assets = await _assetRepository.getAllByRemoteId(album.remoteAssets.map((asset) => asset.remoteId!));
|
||||
album.assets.clear();
|
||||
album.assets.addAll(assets);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/repositories/etag.repository.dart';
|
||||
|
||||
final etagServiceProvider =
|
||||
Provider((ref) => ETagService(ref.watch(etagRepositoryProvider)));
|
||||
final etagServiceProvider = Provider((ref) => ETagService(ref.watch(etagRepositoryProvider)));
|
||||
|
||||
class ETagService {
|
||||
final ETagRepository _eTagRepository;
|
||||
|
||||
@@ -2,8 +2,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/exif.repository.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/exif.provider.dart';
|
||||
|
||||
final exifServiceProvider =
|
||||
Provider((ref) => ExifService(ref.watch(exifRepositoryProvider)));
|
||||
final exifServiceProvider = Provider((ref) => ExifService(ref.watch(exifRepositoryProvider)));
|
||||
|
||||
class ExifService {
|
||||
final IsarExifRepository _exifInfoRepository;
|
||||
|
||||
@@ -30,15 +30,13 @@ class FolderService {
|
||||
fullPath = '/$fullPath';
|
||||
}
|
||||
|
||||
List<String> segments = fullPath.split('/')
|
||||
..removeWhere((s) => s.isEmpty);
|
||||
List<String> segments = fullPath.split('/')..removeWhere((s) => s.isEmpty);
|
||||
|
||||
String currentPath = '';
|
||||
|
||||
for (int i = 0; i < segments.length; i++) {
|
||||
String parentPath = currentPath.isEmpty ? '_root_' : currentPath;
|
||||
currentPath =
|
||||
i == 0 ? '/${segments[i]}' : '$currentPath/${segments[i]}';
|
||||
currentPath = i == 0 ? '/${segments[i]}' : '$currentPath/${segments[i]}';
|
||||
|
||||
if (!folderMap.containsKey(parentPath)) {
|
||||
folderMap[parentPath] = [];
|
||||
@@ -54,26 +52,20 @@ class FolderService {
|
||||
);
|
||||
// Sort folders based on order parameter
|
||||
folderMap[parentPath]!.sort(
|
||||
(a, b) => order == SortOrder.desc
|
||||
? b.name.compareTo(a.name)
|
||||
: a.name.compareTo(b.name),
|
||||
(a, b) => order == SortOrder.desc ? b.name.compareTo(a.name) : a.name.compareTo(b.name),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void attachSubfolders(RecursiveFolder folder) {
|
||||
String fullPath = folder.path.isEmpty
|
||||
? '/${folder.name}'
|
||||
: '${folder.path}/${folder.name}';
|
||||
String fullPath = folder.path.isEmpty ? '/${folder.name}' : '${folder.path}/${folder.name}';
|
||||
|
||||
if (folderMap.containsKey(fullPath)) {
|
||||
folder.subfolders.addAll(folderMap[fullPath]!);
|
||||
// Sort subfolders based on order parameter
|
||||
folder.subfolders.sort(
|
||||
(a, b) => order == SortOrder.desc
|
||||
? b.name.compareTo(a.name)
|
||||
: a.name.compareTo(b.name),
|
||||
(a, b) => order == SortOrder.desc ? b.name.compareTo(a.name) : a.name.compareTo(b.name),
|
||||
);
|
||||
for (var subfolder in folder.subfolders) {
|
||||
attachSubfolders(subfolder);
|
||||
@@ -84,9 +76,7 @@ class FolderService {
|
||||
List<RecursiveFolder> rootSubfolders = folderMap['_root_'] ?? [];
|
||||
// Sort root subfolders based on order parameter
|
||||
rootSubfolders.sort(
|
||||
(a, b) => order == SortOrder.desc
|
||||
? b.name.compareTo(a.name)
|
||||
: a.name.compareTo(b.name),
|
||||
(a, b) => order == SortOrder.desc ? b.name.compareTo(a.name) : a.name.compareTo(b.name),
|
||||
);
|
||||
|
||||
for (var folder in rootSubfolders) {
|
||||
@@ -105,8 +95,7 @@ class FolderService {
|
||||
) async {
|
||||
try {
|
||||
if (folder is RecursiveFolder) {
|
||||
String fullPath =
|
||||
folder.path.isEmpty ? folder.name : '${folder.path}/${folder.name}';
|
||||
String fullPath = folder.path.isEmpty ? folder.name : '${folder.path}/${folder.name}';
|
||||
fullPath = fullPath[0] == '/' ? fullPath.substring(1) : fullPath;
|
||||
var result = await _folderApiRepository.getAssetsForPath(fullPath);
|
||||
|
||||
|
||||
@@ -71,8 +71,7 @@ class GCastService {
|
||||
}
|
||||
|
||||
void _handleMediaStatus(Map<String, dynamic> message) {
|
||||
final statusList =
|
||||
(message['status'] as List).whereType<Map<String, dynamic>>().toList();
|
||||
final statusList = (message['status'] as List).whereType<Map<String, dynamic>>().toList();
|
||||
|
||||
if (statusList.isEmpty) {
|
||||
return;
|
||||
@@ -112,8 +111,7 @@ class GCastService {
|
||||
}
|
||||
|
||||
if (status["currentTime"] != null) {
|
||||
final currentTime =
|
||||
Duration(milliseconds: (status["currentTime"] * 1000 ?? 0).toInt());
|
||||
final currentTime = Duration(milliseconds: (status["currentTime"] * 1000 ?? 0).toInt());
|
||||
onCurrentTime?.call(currentTime);
|
||||
}
|
||||
}
|
||||
@@ -150,8 +148,7 @@ class GCastService {
|
||||
|
||||
// we want to make sure we have at least 10 seconds remaining in the session
|
||||
// this is to account for network latency and other delays when sending the request
|
||||
final bufferedExpiration =
|
||||
tokenExpiration.subtract(const Duration(seconds: 10));
|
||||
final bufferedExpiration = tokenExpiration.subtract(const Duration(seconds: 10));
|
||||
|
||||
return bufferedExpiration.isAfter(DateTime.now());
|
||||
}
|
||||
@@ -181,8 +178,7 @@ class GCastService {
|
||||
type: AssetMediaSize.fullsize,
|
||||
);
|
||||
|
||||
final authenticatedURL =
|
||||
"$unauthenticatedUrl&sessionKey=${sessionKey?.token}";
|
||||
final authenticatedURL = "$unauthenticatedUrl&sessionKey=${sessionKey?.token}";
|
||||
|
||||
// get image mime type
|
||||
final mimeType = await _assetApiRepository.getAssetMIMEType(asset.id);
|
||||
@@ -210,8 +206,7 @@ class GCastService {
|
||||
_mediaStatusPollingTimer?.cancel();
|
||||
|
||||
if (asset.isVideo) {
|
||||
_mediaStatusPollingTimer =
|
||||
Timer.periodic(const Duration(milliseconds: 500), (timer) {
|
||||
_mediaStatusPollingTimer = Timer.periodic(const Duration(milliseconds: 500), (timer) {
|
||||
if (isConnected) {
|
||||
_gCastRepository.sendMessage(CastSession.kNamespaceMedia, {
|
||||
"type": "GET_STATUS",
|
||||
@@ -264,11 +259,7 @@ class GCastService {
|
||||
|
||||
return dests
|
||||
.map(
|
||||
(device) => (
|
||||
device.extras["fn"] ?? "Google Cast",
|
||||
CastDestinationType.googleCast,
|
||||
device
|
||||
),
|
||||
(device) => (device.extras["fn"] ?? "Google Cast", CastDestinationType.googleCast, device),
|
||||
)
|
||||
.where((device) {
|
||||
final caString = device.$3.extras["ca"];
|
||||
|
||||
@@ -13,8 +13,7 @@ class LocalFilesManagerService {
|
||||
|
||||
Future<bool> moveToTrash(List<String> mediaUrls) async {
|
||||
try {
|
||||
return await _channel
|
||||
.invokeMethod('moveToTrash', {'mediaUrls': mediaUrls});
|
||||
return await _channel.invokeMethod('moveToTrash', {'mediaUrls': mediaUrls});
|
||||
} catch (e, s) {
|
||||
_logger.warning('Error moving file to trash', e, s);
|
||||
return false;
|
||||
|
||||
@@ -13,8 +13,7 @@ final localNotificationService = Provider(
|
||||
);
|
||||
|
||||
class LocalNotificationService {
|
||||
final FlutterLocalNotificationsPlugin _localNotificationPlugin =
|
||||
FlutterLocalNotificationsPlugin();
|
||||
final FlutterLocalNotificationsPlugin _localNotificationPlugin = FlutterLocalNotificationsPlugin();
|
||||
final PermissionStatus _permissionStatus;
|
||||
final Ref ref;
|
||||
|
||||
@@ -29,17 +28,14 @@ class LocalNotificationService {
|
||||
static const cancelUploadActionID = 'cancel_upload';
|
||||
|
||||
Future<void> setup() async {
|
||||
const androidSetting =
|
||||
AndroidInitializationSettings('@drawable/notification_icon');
|
||||
const androidSetting = AndroidInitializationSettings('@drawable/notification_icon');
|
||||
const iosSetting = DarwinInitializationSettings();
|
||||
|
||||
const initSettings =
|
||||
InitializationSettings(android: androidSetting, iOS: iosSetting);
|
||||
const initSettings = InitializationSettings(android: androidSetting, iOS: iosSetting);
|
||||
|
||||
await _localNotificationPlugin.initialize(
|
||||
initSettings,
|
||||
onDidReceiveNotificationResponse:
|
||||
_onDidReceiveForegroundNotificationResponse,
|
||||
onDidReceiveNotificationResponse: _onDidReceiveForegroundNotificationResponse,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -35,8 +35,7 @@ class MemoryService {
|
||||
List<Memory> memories = [];
|
||||
|
||||
for (final memory in data) {
|
||||
final dbAssets = await _assetRepository
|
||||
.getAllByRemoteId(memory.assets.map((e) => e.id));
|
||||
final dbAssets = await _assetRepository.getAllByRemoteId(memory.assets.map((e) => e.id));
|
||||
final yearsAgo = now.year - memory.data.year;
|
||||
if (dbAssets.isNotEmpty) {
|
||||
final String title = 'years_ago'.t(
|
||||
@@ -67,8 +66,7 @@ class MemoryService {
|
||||
if (memoryResponse == null) {
|
||||
return null;
|
||||
}
|
||||
final dbAssets = await _assetRepository
|
||||
.getAllByRemoteId(memoryResponse.assets.map((e) => e.id));
|
||||
final dbAssets = await _assetRepository.getAllByRemoteId(memoryResponse.assets.map((e) => e.id));
|
||||
if (dbAssets.isEmpty) {
|
||||
log.warning("No assets found for memory with ID: $id");
|
||||
return null;
|
||||
|
||||
@@ -45,8 +45,7 @@ class PartnerService {
|
||||
Future<bool> removePartner(UserDto partner) async {
|
||||
try {
|
||||
await _partnerApiRepository.delete(partner.id);
|
||||
await _isarUserRepository
|
||||
.update(partner.copyWith(isPartnerSharedBy: false));
|
||||
await _isarUserRepository.update(partner.copyWith(isPartnerSharedBy: false));
|
||||
} catch (e) {
|
||||
_log.warning("Failed to remove partner ${partner.id}", e);
|
||||
return false;
|
||||
@@ -57,8 +56,7 @@ class PartnerService {
|
||||
Future<bool> addPartner(UserDto partner) async {
|
||||
try {
|
||||
await _partnerApiRepository.create(partner.id);
|
||||
await _isarUserRepository
|
||||
.update(partner.copyWith(isPartnerSharedBy: true));
|
||||
await _isarUserRepository.update(partner.copyWith(isPartnerSharedBy: true));
|
||||
return true;
|
||||
} catch (e) {
|
||||
_log.warning("Failed to add partner ${partner.id}", e);
|
||||
@@ -75,8 +73,7 @@ class PartnerService {
|
||||
partner.id,
|
||||
inTimeline: inTimeline,
|
||||
);
|
||||
await _isarUserRepository
|
||||
.update(partner.copyWith(inTimeline: dto.inTimeline));
|
||||
await _isarUserRepository.update(partner.copyWith(inTimeline: dto.inTimeline));
|
||||
return true;
|
||||
} catch (e) {
|
||||
_log.warning("Failed to update partner ${partner.id}", e);
|
||||
|
||||
@@ -40,8 +40,7 @@ class PersonService {
|
||||
Future<List<Asset>> getPersonAssets(String id) async {
|
||||
try {
|
||||
final assets = await _assetApiRepository.search(personIds: [id]);
|
||||
return await _assetRepository
|
||||
.getAllByRemoteId(assets.map((a) => a.remoteId!));
|
||||
return await _assetRepository.getAllByRemoteId(assets.map((a) => a.remoteId!));
|
||||
} catch (error, stack) {
|
||||
_log.severe("Error while fetching person assets", error, stack);
|
||||
}
|
||||
|
||||
@@ -61,8 +61,7 @@ class SearchService {
|
||||
}
|
||||
|
||||
return SearchResult(
|
||||
assets: await _assetRepository
|
||||
.getAllByRemoteId(response.assets.items.map((e) => e.id)),
|
||||
assets: await _assetRepository.getAllByRemoteId(response.assets.items.map((e) => e.id)),
|
||||
nextPage: response.assets.nextPage?.toInt(),
|
||||
);
|
||||
} catch (error, stackTrace) {
|
||||
|
||||
@@ -10,8 +10,7 @@ import 'package:path_provider/path_provider.dart';
|
||||
import 'package:share_plus/share_plus.dart';
|
||||
import 'api.service.dart';
|
||||
|
||||
final shareServiceProvider =
|
||||
Provider((ref) => ShareService(ref.watch(apiServiceProvider)));
|
||||
final shareServiceProvider = Provider((ref) => ShareService(ref.watch(apiServiceProvider)));
|
||||
|
||||
class ShareService {
|
||||
final ApiService _apiService;
|
||||
@@ -37,8 +36,7 @@ class ShareService {
|
||||
final tempDir = await getTemporaryDirectory();
|
||||
final fileName = asset.fileName;
|
||||
final tempFile = await File('${tempDir.path}/$fileName').create();
|
||||
final res = await _apiService.assetsApi
|
||||
.downloadAssetWithHttpInfo(asset.remoteId!);
|
||||
final res = await _apiService.assetsApi.downloadAssetWithHttpInfo(asset.remoteId!);
|
||||
|
||||
if (res.statusCode != 200) {
|
||||
_log.severe(
|
||||
|
||||
@@ -18,9 +18,7 @@ class SharedLinkService {
|
||||
Future<AsyncValue<List<SharedLink>>> getAllSharedLinks() async {
|
||||
try {
|
||||
final list = await _apiService.sharedLinksApi.getAllSharedLinks();
|
||||
return list != null
|
||||
? AsyncData(list.map(SharedLink.fromDto).toList())
|
||||
: const AsyncData([]);
|
||||
return list != null ? AsyncData(list.map(SharedLink.fromDto).toList()) : const AsyncData([]);
|
||||
} catch (e, stack) {
|
||||
_log.severe("Failed to fetch shared links", e, stack);
|
||||
return AsyncError(e, stack);
|
||||
@@ -46,8 +44,7 @@ class SharedLinkService {
|
||||
DateTime? expiresAt,
|
||||
}) async {
|
||||
try {
|
||||
final type =
|
||||
albumId != null ? SharedLinkType.ALBUM : SharedLinkType.INDIVIDUAL;
|
||||
final type = albumId != null ? SharedLinkType.ALBUM : SharedLinkType.INDIVIDUAL;
|
||||
SharedLinkCreateDto? dto;
|
||||
if (type == SharedLinkType.ALBUM) {
|
||||
dto = SharedLinkCreateDto(
|
||||
@@ -74,8 +71,7 @@ class SharedLinkService {
|
||||
}
|
||||
|
||||
if (dto != null) {
|
||||
final responseDto =
|
||||
await _apiService.sharedLinksApi.createSharedLink(dto);
|
||||
final responseDto = await _apiService.sharedLinksApi.createSharedLink(dto);
|
||||
if (responseDto != null) {
|
||||
return SharedLink.fromDto(responseDto);
|
||||
}
|
||||
|
||||
@@ -60,8 +60,7 @@ class StackService {
|
||||
|
||||
removeAssets.add(asset);
|
||||
}
|
||||
await _assetRepository
|
||||
.transaction(() => _assetRepository.updateAll(removeAssets));
|
||||
await _assetRepository.transaction(() => _assetRepository.updateAll(removeAssets));
|
||||
} catch (error) {
|
||||
debugPrint("Error while deleting stack: $error");
|
||||
}
|
||||
|
||||
@@ -94,8 +94,7 @@ class SyncService {
|
||||
|
||||
/// Syncs users from the server to the local database
|
||||
/// Returns `true`if there were any changes
|
||||
Future<bool> syncUsersFromServer(List<UserDto> users) =>
|
||||
_lock.run(() => _syncUsersFromServer(users));
|
||||
Future<bool> syncUsersFromServer(List<UserDto> users) => _lock.run(() => _syncUsersFromServer(users));
|
||||
|
||||
/// Syncs remote assets owned by the logged-in user to the DB
|
||||
/// Returns `true` if there were any changes
|
||||
@@ -105,8 +104,7 @@ class SyncService {
|
||||
List<UserDto> users,
|
||||
DateTime since,
|
||||
) getChangedAssets,
|
||||
required FutureOr<List<Asset>?> Function(UserDto user, DateTime until)
|
||||
loadAssets,
|
||||
required FutureOr<List<Asset>?> Function(UserDto user, DateTime until) loadAssets,
|
||||
}) =>
|
||||
_lock.run(
|
||||
() async =>
|
||||
@@ -139,18 +137,13 @@ class SyncService {
|
||||
}
|
||||
deleteCandidates.sort(Asset.compareById);
|
||||
existing.sort(Asset.compareById);
|
||||
return _diffAssets(existing, deleteCandidates, compare: Asset.compareById)
|
||||
.$3
|
||||
.map((e) => e.id)
|
||||
.toList();
|
||||
return _diffAssets(existing, deleteCandidates, compare: Asset.compareById).$3.map((e) => e.id).toList();
|
||||
}
|
||||
|
||||
/// Syncs a new asset to the db. Returns `true` if successful
|
||||
Future<bool> syncNewAssetToDb(Asset newAsset) =>
|
||||
_lock.run(() => _syncNewAssetToDb(newAsset));
|
||||
Future<bool> syncNewAssetToDb(Asset newAsset) => _lock.run(() => _syncNewAssetToDb(newAsset));
|
||||
|
||||
Future<bool> removeAllLocalAlbumsAndAssets() =>
|
||||
_lock.run(_removeAllLocalAlbumsAndAssets);
|
||||
Future<bool> removeAllLocalAlbumsAndAssets() => _lock.run(_removeAllLocalAlbumsAndAssets);
|
||||
|
||||
// private methods:
|
||||
|
||||
@@ -189,8 +182,7 @@ class SyncService {
|
||||
|
||||
/// Syncs a new asset to the db. Returns `true` if successful
|
||||
Future<bool> _syncNewAssetToDb(Asset a) async {
|
||||
final Asset? inDb =
|
||||
await _assetRepository.getByOwnerIdChecksum(a.ownerId, a.checksum);
|
||||
final Asset? inDb = await _assetRepository.getByOwnerIdChecksum(a.ownerId, a.checksum);
|
||||
if (inDb != null) {
|
||||
// unify local/remote assets by replacing the
|
||||
// local-only asset in the DB with a local&remote asset
|
||||
@@ -214,8 +206,7 @@ class SyncService {
|
||||
) getChangedAssets,
|
||||
) async {
|
||||
final currentUser = _userService.getMyUser();
|
||||
final DateTime? since =
|
||||
(await _eTagRepository.get(currentUser.id))?.time?.toUtc();
|
||||
final DateTime? since = (await _eTagRepository.get(currentUser.id))?.time?.toUtc();
|
||||
if (since == null) return null;
|
||||
final DateTime now = DateTime.now();
|
||||
final (toUpsert, toDelete) = await getChangedAssets(users, since);
|
||||
@@ -244,13 +235,10 @@ class SyncService {
|
||||
|
||||
Future<void> _moveToTrashMatchedAssets(Iterable<String> idsToDelete) async {
|
||||
final List<Asset> localAssets = await _assetRepository.getAllLocal();
|
||||
final List<Asset> matchedAssets = localAssets
|
||||
.where((asset) => idsToDelete.contains(asset.remoteId))
|
||||
.toList();
|
||||
final List<Asset> matchedAssets = localAssets.where((asset) => idsToDelete.contains(asset.remoteId)).toList();
|
||||
|
||||
final mediaUrls = await Future.wait(
|
||||
matchedAssets
|
||||
.map((asset) => asset.local?.getMediaUrl() ?? Future.value(null)),
|
||||
matchedAssets.map((asset) => asset.local?.getMediaUrl() ?? Future.value(null)),
|
||||
);
|
||||
|
||||
await _localFilesManager.moveToTrash(mediaUrls.nonNulls.toList());
|
||||
@@ -371,10 +359,8 @@ class SyncService {
|
||||
final bool changes = await diffSortedLists(
|
||||
remoteAlbums,
|
||||
dbAlbums,
|
||||
compare: (remoteAlbum, dbAlbum) =>
|
||||
remoteAlbum.remoteId!.compareTo(dbAlbum.remoteId!),
|
||||
both: (remoteAlbum, dbAlbum) =>
|
||||
_syncRemoteAlbum(remoteAlbum, dbAlbum, toDelete, existing),
|
||||
compare: (remoteAlbum, dbAlbum) => remoteAlbum.remoteId!.compareTo(dbAlbum.remoteId!),
|
||||
both: (remoteAlbum, dbAlbum) => _syncRemoteAlbum(remoteAlbum, dbAlbum, toDelete, existing),
|
||||
onlyFirst: (remoteAlbum) => _addAlbumFromServer(remoteAlbum, existing),
|
||||
onlySecond: (dbAlbum) => _removeAlbumFromDb(dbAlbum, toDelete),
|
||||
);
|
||||
@@ -421,11 +407,9 @@ class SyncService {
|
||||
);
|
||||
|
||||
// update shared users
|
||||
final List<UserDto> sharedUsers =
|
||||
album.sharedUsers.map((u) => u.toDto()).toList(growable: false);
|
||||
final List<UserDto> sharedUsers = album.sharedUsers.map((u) => u.toDto()).toList(growable: false);
|
||||
sharedUsers.sort((a, b) => a.id.compareTo(b.id));
|
||||
final List<UserDto> users = dto.remoteUsers.map((u) => u.toDto()).toList()
|
||||
..sort((a, b) => a.id.compareTo(b.id));
|
||||
final List<UserDto> users = dto.remoteUsers.map((u) => u.toDto()).toList()..sort((a, b) => a.id.compareTo(b.id));
|
||||
final List<String> userIdsToAdd = [];
|
||||
final List<UserDto> usersToUnlink = [];
|
||||
diffSortedListsSync(
|
||||
@@ -456,10 +440,8 @@ class SyncService {
|
||||
album.sortOrder = dto.sortOrder;
|
||||
|
||||
final remoteThumbnailAssetId = dto.remoteThumbnailAssetId;
|
||||
if (remoteThumbnailAssetId != null &&
|
||||
album.thumbnail.value?.remoteId != remoteThumbnailAssetId) {
|
||||
album.thumbnail.value =
|
||||
await _assetRepository.getByRemoteId(remoteThumbnailAssetId);
|
||||
if (remoteThumbnailAssetId != null && album.thumbnail.value?.remoteId != remoteThumbnailAssetId) {
|
||||
album.thumbnail.value = await _assetRepository.getByRemoteId(remoteThumbnailAssetId);
|
||||
}
|
||||
|
||||
// write & commit all changes to DB
|
||||
@@ -480,8 +462,7 @@ class SyncService {
|
||||
|
||||
if (album.shared || dto.shared) {
|
||||
final userId = (_userService.getMyUser()).id;
|
||||
final foreign =
|
||||
await _assetRepository.getByAlbum(album, notOwnedBy: [userId]);
|
||||
final foreign = await _assetRepository.getByAlbum(album, notOwnedBy: [userId]);
|
||||
existing.addAll(foreign);
|
||||
|
||||
// delete assets in DB unless they belong to this user or part of some other shared album
|
||||
@@ -505,16 +486,14 @@ class SyncService {
|
||||
if (album.remoteAssetCount == album.remoteAssets.length) {
|
||||
// in case an album contains assets not yet present in local DB:
|
||||
// put missing album assets into local DB
|
||||
final (existingInDb, updated) =
|
||||
await _linkWithExistingFromDb(album.remoteAssets.toList());
|
||||
final (existingInDb, updated) = await _linkWithExistingFromDb(album.remoteAssets.toList());
|
||||
existing.addAll(existingInDb);
|
||||
await upsertAssetsWithExif(updated);
|
||||
|
||||
await _entityService.fillAlbumWithDatabaseEntities(album);
|
||||
await _albumRepository.create(album);
|
||||
} else {
|
||||
_log.warning(
|
||||
"Failed to add album from server: assetCount ${album.remoteAssetCount} != "
|
||||
_log.warning("Failed to add album from server: assetCount ${album.remoteAssetCount} != "
|
||||
"asset array length ${album.remoteAssets.length} for album ${album.name}");
|
||||
}
|
||||
}
|
||||
@@ -534,8 +513,7 @@ class SyncService {
|
||||
} else if (album.shared) {
|
||||
// delete assets in DB unless they belong to this user or are part of some other shared album or belong to a partner
|
||||
final userIds = (await _getAllAccessibleUsers()).map((user) => user.id);
|
||||
final orphanedAssets =
|
||||
await _assetRepository.getByAlbum(album, notOwnedBy: userIds);
|
||||
final orphanedAssets = await _assetRepository.getByAlbum(album, notOwnedBy: userIds);
|
||||
deleteCandidates.addAll(orphanedAssets);
|
||||
}
|
||||
try {
|
||||
@@ -553,8 +531,7 @@ class SyncService {
|
||||
Set<String>? excludedAssets,
|
||||
]) async {
|
||||
onDevice.sort((a, b) => a.localId!.compareTo(b.localId!));
|
||||
final inDb =
|
||||
await _albumRepository.getAll(remote: false, sortBy: AlbumSort.localId);
|
||||
final inDb = await _albumRepository.getAll(remote: false, sortBy: AlbumSort.localId);
|
||||
final List<Asset> deleteCandidates = [];
|
||||
final List<Asset> existing = [];
|
||||
final bool anyChanges = await diffSortedLists(
|
||||
@@ -574,8 +551,7 @@ class SyncService {
|
||||
_log.fine(
|
||||
"Syncing all local albums almost done. Collected ${deleteCandidates.length} asset candidates to delete",
|
||||
);
|
||||
final (toDelete, toUpdate) =
|
||||
_handleAssetRemoval(deleteCandidates, existing, remote: false);
|
||||
final (toDelete, toUpdate) = _handleAssetRemoval(deleteCandidates, existing, remote: false);
|
||||
_log.fine(
|
||||
"${toDelete.length} assets to delete, ${toUpdate.length} to update",
|
||||
);
|
||||
@@ -610,9 +586,7 @@ class SyncService {
|
||||
return false;
|
||||
}
|
||||
_log.info("Local album ${deviceAlbum.name} has changed. Syncing...");
|
||||
if (!forceRefresh &&
|
||||
excludedAssets == null &&
|
||||
await _syncDeviceAlbumFast(deviceAlbum, dbAlbum)) {
|
||||
if (!forceRefresh && excludedAssets == null && await _syncDeviceAlbumFast(deviceAlbum, dbAlbum)) {
|
||||
_log.info("Fast synced local album ${deviceAlbum.name} to DB");
|
||||
return true;
|
||||
}
|
||||
@@ -624,8 +598,7 @@ class SyncService {
|
||||
);
|
||||
|
||||
assert(inDb.isSorted(Asset.compareByChecksum), "inDb not sorted!");
|
||||
final int assetCountOnDevice =
|
||||
await _albumMediaRepository.getAssetCount(deviceAlbum.localId!);
|
||||
final int assetCountOnDevice = await _albumMediaRepository.getAssetCount(deviceAlbum.localId!);
|
||||
final List<Asset> onDevice = await _getHashedAssets(
|
||||
deviceAlbum,
|
||||
excludedAssets: excludedAssets,
|
||||
@@ -643,9 +616,7 @@ class SyncService {
|
||||
_log.info(
|
||||
"Only excluded assets in local album ${deviceAlbum.name} changed. Stopping sync.",
|
||||
);
|
||||
if (assetCountOnDevice !=
|
||||
(await _eTagRepository.getById(deviceAlbum.eTagKeyAssetCount))
|
||||
?.assetCount) {
|
||||
if (assetCountOnDevice != (await _eTagRepository.getById(deviceAlbum.eTagKeyAssetCount))?.assetCount) {
|
||||
await _eTagRepository.upsertAll([
|
||||
ETag(
|
||||
id: deviceAlbum.eTagKeyAssetCount,
|
||||
@@ -667,8 +638,7 @@ class SyncService {
|
||||
dbAlbum.name = deviceAlbum.name;
|
||||
dbAlbum.description = deviceAlbum.description;
|
||||
dbAlbum.modifiedAt = deviceAlbum.modifiedAt;
|
||||
if (dbAlbum.thumbnail.value != null &&
|
||||
toDelete.contains(dbAlbum.thumbnail.value)) {
|
||||
if (dbAlbum.thumbnail.value != null && toDelete.contains(dbAlbum.thumbnail.value)) {
|
||||
dbAlbum.thumbnail.value = null;
|
||||
}
|
||||
try {
|
||||
@@ -702,12 +672,8 @@ class SyncService {
|
||||
);
|
||||
return false;
|
||||
}
|
||||
final int totalOnDevice =
|
||||
await _albumMediaRepository.getAssetCount(deviceAlbum.localId!);
|
||||
final int lastKnownTotal =
|
||||
(await _eTagRepository.getById(deviceAlbum.eTagKeyAssetCount))
|
||||
?.assetCount ??
|
||||
0;
|
||||
final int totalOnDevice = await _albumMediaRepository.getAssetCount(deviceAlbum.localId!);
|
||||
final int lastKnownTotal = (await _eTagRepository.getById(deviceAlbum.eTagKeyAssetCount))?.assetCount ?? 0;
|
||||
if (totalOnDevice <= lastKnownTotal) {
|
||||
_log.info(
|
||||
"Local album ${deviceAlbum.name} totalOnDevice is less than lastKnownTotal. Skipping sync.",
|
||||
@@ -776,8 +742,7 @@ class SyncService {
|
||||
album.thumbnail.value = thumb;
|
||||
try {
|
||||
await _albumRepository.create(album);
|
||||
final int assetCount =
|
||||
await _albumMediaRepository.getAssetCount(album.localId!);
|
||||
final int assetCount = await _albumMediaRepository.getAssetCount(album.localId!);
|
||||
await _eTagRepository.upsertAll([
|
||||
ETag(id: album.eTagKeyAssetCount, assetCount: assetCount),
|
||||
]);
|
||||
@@ -913,9 +878,8 @@ class SyncService {
|
||||
modifiedFrom: modifiedFrom,
|
||||
modifiedUntil: modifiedUntil,
|
||||
);
|
||||
final filtered = excludedAssets == null
|
||||
? entities
|
||||
: entities.where((e) => !excludedAssets.contains(e.localId!)).toList();
|
||||
final filtered =
|
||||
excludedAssets == null ? entities : entities.where((e) => !excludedAssets.contains(e.localId!)).toList();
|
||||
return _hashService.hashAssets(filtered);
|
||||
}
|
||||
|
||||
@@ -942,15 +906,13 @@ class SyncService {
|
||||
deviceAlbum.description != dbAlbum.description ||
|
||||
!deviceAlbum.modifiedAt.isAtSameMomentAs(dbAlbum.modifiedAt) ||
|
||||
await _albumMediaRepository.getAssetCount(deviceAlbum.localId!) !=
|
||||
(await _eTagRepository.getById(deviceAlbum.eTagKeyAssetCount))
|
||||
?.assetCount;
|
||||
(await _eTagRepository.getById(deviceAlbum.eTagKeyAssetCount))?.assetCount;
|
||||
}
|
||||
|
||||
Future<bool> _removeAllLocalAlbumsAndAssets() async {
|
||||
try {
|
||||
final assets = await _assetRepository.getAllLocal();
|
||||
final (toDelete, toUpdate) =
|
||||
_handleAssetRemoval(assets, [], remote: false);
|
||||
final (toDelete, toUpdate) = _handleAssetRemoval(assets, [], remote: false);
|
||||
await _assetRepository.transaction(() async {
|
||||
await _assetRepository.deleteByIds(toDelete);
|
||||
await _assetRepository.updateAll(toUpdate);
|
||||
@@ -971,10 +933,8 @@ class SyncService {
|
||||
_log.warning("Failed to fetch users", e);
|
||||
users = null;
|
||||
}
|
||||
final List<UserDto> sharedBy =
|
||||
await _partnerApiRepository.getAll(Direction.sharedByMe);
|
||||
final List<UserDto> sharedWith =
|
||||
await _partnerApiRepository.getAll(Direction.sharedWithMe);
|
||||
final List<UserDto> sharedBy = await _partnerApiRepository.getAll(Direction.sharedByMe);
|
||||
final List<UserDto> sharedWith = await _partnerApiRepository.getAll(Direction.sharedWithMe);
|
||||
|
||||
if (users == null) {
|
||||
_log.warning("Failed to refresh users");
|
||||
|
||||
@@ -103,8 +103,7 @@ class TimelineService {
|
||||
}
|
||||
|
||||
GroupAssetsBy _getGroupByOption() {
|
||||
return GroupAssetsBy
|
||||
.values[_appSettingsService.getSetting(AppSettingsEnum.groupAssetsBy)];
|
||||
return GroupAssetsBy.values[_appSettingsService.getSetting(AppSettingsEnum.groupAssetsBy)];
|
||||
}
|
||||
|
||||
Stream<RenderList> watchLockedTimelineProvider() async* {
|
||||
|
||||
@@ -20,14 +20,11 @@ class UploadService {
|
||||
void Function(TaskStatusUpdate)? onUploadStatus;
|
||||
void Function(TaskProgressUpdate)? onTaskProgress;
|
||||
|
||||
final StreamController<TaskStatusUpdate> _taskStatusController =
|
||||
StreamController<TaskStatusUpdate>.broadcast();
|
||||
final StreamController<TaskProgressUpdate> _taskProgressController =
|
||||
StreamController<TaskProgressUpdate>.broadcast();
|
||||
final StreamController<TaskStatusUpdate> _taskStatusController = StreamController<TaskStatusUpdate>.broadcast();
|
||||
final StreamController<TaskProgressUpdate> _taskProgressController = StreamController<TaskProgressUpdate>.broadcast();
|
||||
|
||||
Stream<TaskStatusUpdate> get taskStatusStream => _taskStatusController.stream;
|
||||
Stream<TaskProgressUpdate> get taskProgressStream =>
|
||||
_taskProgressController.stream;
|
||||
Stream<TaskProgressUpdate> get taskProgressStream => _taskProgressController.stream;
|
||||
|
||||
UploadService(
|
||||
this._uploadRepository,
|
||||
@@ -103,8 +100,7 @@ class UploadService {
|
||||
final headers = ApiService.getRequestHeaders();
|
||||
final deviceId = Store.get(StoreKey.deviceId);
|
||||
|
||||
final (baseDirectory, directory, filename) =
|
||||
await Task.split(filePath: file.path);
|
||||
final (baseDirectory, directory, filename) = await Task.split(filePath: file.path);
|
||||
final stats = await file.stat();
|
||||
final fileCreatedAt = stats.changed;
|
||||
final fileModifiedAt = stats.modified;
|
||||
|
||||
Reference in New Issue
Block a user