Compare commits

...

2 Commits

Author SHA1 Message Date
Alex f5f38f3a6c chore: more roburst local sync execution 2025-07-05 14:33:50 -05:00
OffsetMonkey538 2f5d75ce21 docs: fix typo of webp listed under jpeg (#19743) 2025-07-05 15:52:22 +00:00
3 changed files with 92 additions and 34 deletions
+1 -1
View File
@@ -16,7 +16,7 @@ For the full list, refer to the [Immich source code](https://github.com/immich-a
| `HEIC` | `.heic` | :white_check_mark: | | | `HEIC` | `.heic` | :white_check_mark: | |
| `HEIF` | `.heif` | :white_check_mark: | | | `HEIF` | `.heif` | :white_check_mark: | |
| `JPEG 2000` | `.jp2` | :white_check_mark: | | | `JPEG 2000` | `.jp2` | :white_check_mark: | |
| `JPEG` | `.webp` `.jpg` `.jpe` `.insp` | :white_check_mark: | | | `JPEG` | `.jpeg` `.jpg` `.jpe` `.insp` | :white_check_mark: | |
| `JPEG XL` | `.jxl` | :white_check_mark: | | | `JPEG XL` | `.jxl` | :white_check_mark: | |
| `PNG` | `.png` | :white_check_mark: | | | `PNG` | `.png` | :white_check_mark: | |
| `PSD` | `.psd` | :white_check_mark: | Adobe Photoshop | | `PSD` | `.psd` | :white_check_mark: | Adobe Photoshop |
+87 -32
View File
@@ -9,8 +9,57 @@ class BackgroundSyncManager {
Cancelable<void>? _deviceAlbumSyncTask; Cancelable<void>? _deviceAlbumSyncTask;
Cancelable<void>? _hashTask; Cancelable<void>? _hashTask;
Completer<void>? _localSyncMutex;
Completer<void>? _remoteSyncMutex;
Completer<void>? _hashMutex;
BackgroundSyncManager(); BackgroundSyncManager();
Future<T> _withMutex<T>(
Completer<void>? Function() getMutex,
void Function(Completer<void>?) setMutex,
Future<T> Function() operation,
) async {
while (getMutex() != null) {
await getMutex()!.future;
}
final mutex = Completer<void>();
setMutex(mutex);
try {
final result = await operation();
return result;
} finally {
setMutex(null);
mutex.complete();
}
}
Future<T> _withLocalSyncMutex<T>(Future<T> Function() operation) {
return _withMutex(
() => _localSyncMutex,
(mutex) => _localSyncMutex = mutex,
operation,
);
}
Future<T> _withRemoteSyncMutex<T>(Future<T> Function() operation) {
return _withMutex(
() => _remoteSyncMutex,
(mutex) => _remoteSyncMutex = mutex,
operation,
);
}
Future<T> _withHashMutex<T>(Future<T> Function() operation) {
return _withMutex(
() => _hashMutex,
(mutex) => _hashMutex = mutex,
operation,
);
}
Future<void> cancel() { Future<void> cancel() {
final futures = <Future>[]; final futures = <Future>[];
@@ -25,51 +74,57 @@ class BackgroundSyncManager {
// No need to cancel the task, as it can also be run when the user logs out // No need to cancel the task, as it can also be run when the user logs out
Future<void> syncLocal({bool full = false}) { Future<void> syncLocal({bool full = false}) {
if (_deviceAlbumSyncTask != null) { return _withLocalSyncMutex(() async {
return _deviceAlbumSyncTask!.future; if (_deviceAlbumSyncTask != null) {
} return _deviceAlbumSyncTask!.future;
}
// We use a ternary operator to avoid [_deviceAlbumSyncTask] from being // We use a ternary operator to avoid [_deviceAlbumSyncTask] from being
// captured by the closure passed to [runInIsolateGentle]. // captured by the closure passed to [runInIsolateGentle].
_deviceAlbumSyncTask = full _deviceAlbumSyncTask = full
? runInIsolateGentle( ? runInIsolateGentle(
computation: (ref) => computation: (ref) =>
ref.read(localSyncServiceProvider).sync(full: true), ref.read(localSyncServiceProvider).sync(full: true),
) )
: runInIsolateGentle( : runInIsolateGentle(
computation: (ref) => computation: (ref) =>
ref.read(localSyncServiceProvider).sync(full: false), ref.read(localSyncServiceProvider).sync(full: false),
); );
return _deviceAlbumSyncTask!.whenComplete(() { return _deviceAlbumSyncTask!.whenComplete(() {
_deviceAlbumSyncTask = null; _deviceAlbumSyncTask = null;
});
}); });
} }
// No need to cancel the task, as it can also be run when the user logs out // No need to cancel the task, as it can also be run when the user logs out
Future<void> hashAssets() { Future<void> hashAssets() {
if (_hashTask != null) { return _withHashMutex(() async {
return _hashTask!.future; if (_hashTask != null) {
} return _hashTask!.future;
}
_hashTask = runInIsolateGentle( _hashTask = runInIsolateGentle(
computation: (ref) => ref.read(hashServiceProvider).hashAssets(), computation: (ref) => ref.read(hashServiceProvider).hashAssets(),
); );
return _hashTask!.whenComplete(() { return _hashTask!.whenComplete(() {
_hashTask = null; _hashTask = null;
});
}); });
} }
Future<void> syncRemote() { Future<void> syncRemote() {
if (_syncTask != null) { return _withRemoteSyncMutex(() async {
return _syncTask!.future; if (_syncTask != null) {
} return _syncTask!.future;
}
_syncTask = runInIsolateGentle( _syncTask = runInIsolateGentle(
computation: (ref) => ref.read(syncStreamServiceProvider).sync(), computation: (ref) => ref.read(syncStreamServiceProvider).sync(),
); );
return _syncTask!.whenComplete(() { return _syncTask!.whenComplete(() {
_syncTask = null; _syncTask = null;
});
}); });
} }
} }
@@ -73,7 +73,10 @@ class ImmichSliverAppBar extends ConsumerWidget {
onPressed: () => context.pop(), onPressed: () => context.pop(),
), ),
IconButton( IconButton(
onPressed: () => ref.read(backgroundSyncProvider).syncRemote(), onPressed: () {
ref.read(backgroundSyncProvider).syncLocal(full: true);
ref.read(backgroundSyncProvider).syncRemote();
},
icon: const Icon( icon: const Icon(
Icons.sync, Icons.sync,
), ),