From fc619bbd034bcf2b810f9a1d0962bb87e15f8e9f Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Sat, 22 Mar 2025 21:53:31 +0530 Subject: [PATCH] Use task queue to throttle local thumbnail fetch --- .../lib/ui/viewer/file/thumbnail_widget.dart | 5 ++ mobile/lib/utils/thumbnail_util.dart | 58 +++++++++++++++---- 2 files changed, 53 insertions(+), 10 deletions(-) diff --git a/mobile/lib/ui/viewer/file/thumbnail_widget.dart b/mobile/lib/ui/viewer/file/thumbnail_widget.dart index 4ace74da94..2876255e51 100644 --- a/mobile/lib/ui/viewer/file/thumbnail_widget.dart +++ b/mobile/lib/ui/viewer/file/thumbnail_widget.dart @@ -286,6 +286,11 @@ class _ThumbnailWidgetState extends State { }).catchError((e) { _logger.warning("Could not load image: ", e); _errorLoadingLocalThumbnail = true; + + if (mounted) { + _reset(); + setState(() {}); + } }); } diff --git a/mobile/lib/utils/thumbnail_util.dart b/mobile/lib/utils/thumbnail_util.dart index e491c0e2b2..008225dc33 100644 --- a/mobile/lib/utils/thumbnail_util.dart +++ b/mobile/lib/utils/thumbnail_util.dart @@ -17,6 +17,7 @@ import "package:photos/services/collections_service.dart"; import "package:photos/utils/file_key.dart"; import 'package:photos/utils/file_uploader_util.dart'; import 'package:photos/utils/file_util.dart'; +import "package:photos/utils/standalone/task_queue.dart"; final _logger = Logger("ThumbnailUtil"); final _uploadIDToDownloadItem = {}; @@ -92,6 +93,13 @@ Future getThumbnailFromServer(EnteFile file) async { } } +final thumbnailQueue = TaskQueue( + maxConcurrentTasks: 15, + taskTimeout: const Duration(minutes: 1), + maxQueueSize: 100, // Limit the queue to 50 pending tasks +); + +final Set _fetchedThisSession = {}; Future getThumbnailFromLocal( EnteFile file, { int size = thumbnailSmallSize, @@ -110,17 +118,47 @@ Future getThumbnailFromLocal( return data; }); } else { - return file.getAsset.then((asset) async { - if (asset == null || !(await asset.exists)) { - return null; - } - return asset - .thumbnailDataWithSize(ThumbnailSize(size, size), quality: quality) - .then((data) { - ThumbnailInMemoryLruCache.put(file, data, size); - return data; + // Use file.localID as the queue ID + final String queueId = file.localID ?? ''; + if (_fetchedThisSession.contains(queueId)) { + return file.getAsset.then((asset) async { + if (asset == null || !(await asset.exists)) { + return null; + } + return asset + .thumbnailDataWithSize(ThumbnailSize(size, size), quality: quality) + .then((data) { + ThumbnailInMemoryLruCache.put(file, data, size); + return data; + }); }); - }); + } + try { + // Add the task to the queue and await its completion + _logger.info( + 'Queue stats Pending ${thumbnailQueue.pendingTasksCount} runningTasks ${thumbnailQueue.runningTasksCount}', + ); + await thumbnailQueue.addTask(queueId, () async { + final asset = await file.getAsset; + if (asset == null || !(await asset.exists)) { + return; + } + final data = await asset + .thumbnailDataWithSize(ThumbnailSize(size, size), quality: quality); + _fetchedThisSession.add(queueId); + ThumbnailInMemoryLruCache.put(file, data, size); + }); + // If we get here, the task completed successfully, retrieve from cache + return ThumbnailInMemoryLruCache.get(file, size); + } catch (e) { + if (e is TaskQueueTimeoutException || e is TaskQueueOverflowException) { + _logger.info('localThumb timeout or cancelled for ${file.localID}', e); + rethrow; + } else { + _logger.warning('localThumb failed for ${file.localID}', e); + } + return null; + } } }