Add thumb queue
This commit is contained in:
@@ -7,6 +7,19 @@ import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/painting.dart';
|
||||
import 'package:photo_manager/photo_manager.dart';
|
||||
import "package:photos/image/in_memory_image_cache.dart";
|
||||
import "package:photos/utils/standalone/task_queue.dart";
|
||||
|
||||
final thumbnailQueue = TaskQueue<String>(
|
||||
maxConcurrentTasks: 15,
|
||||
taskTimeout: const Duration(minutes: 1),
|
||||
maxQueueSize: 1000, // Limit the queue to 50 pending tasks
|
||||
);
|
||||
|
||||
final mediumThumbnailQueue = TaskQueue<String>(
|
||||
maxConcurrentTasks: 5,
|
||||
taskTimeout: const Duration(minutes: 1),
|
||||
maxQueueSize: 1000, // Limit the queue to 50 pending tasks
|
||||
);
|
||||
|
||||
class LocalThumbnailProvider extends ImageProvider<LocalThumbnailProviderKey> {
|
||||
final LocalThumbnailProviderKey key;
|
||||
@@ -36,6 +49,11 @@ class LocalThumbnailProvider extends ImageProvider<LocalThumbnailProviderKey> {
|
||||
);
|
||||
}
|
||||
|
||||
static Future<void> cancelRequest(LocalThumbnailProviderKey key) async {
|
||||
thumbnailQueue.removeTask('${key.asset.id}-small');
|
||||
mediumThumbnailQueue.removeTask('${key.asset.id}-medium');
|
||||
}
|
||||
|
||||
Stream<ui.Codec> _codec(
|
||||
LocalThumbnailProviderKey key,
|
||||
ImageDecoderCallback decode,
|
||||
@@ -56,10 +74,16 @@ class LocalThumbnailProvider extends ImageProvider<LocalThumbnailProviderKey> {
|
||||
Uint8List? thumbBytes =
|
||||
enteImageCache.getThumbByID(asset.id, key.smallThumbWidth);
|
||||
if (thumbBytes == null) {
|
||||
thumbBytes = await asset.thumbnailDataWithSize(
|
||||
ThumbnailSize(key.smallThumbWidth, key.smallThumbHeight),
|
||||
quality: 75,
|
||||
);
|
||||
final Completer<Uint8List?> future = Completer();
|
||||
await thumbnailQueue.addTask('${asset.id}-small', () async {
|
||||
final thumbBytes = await asset.thumbnailDataWithSize(
|
||||
ThumbnailSize(key.smallThumbWidth, key.smallThumbHeight),
|
||||
quality: 75,
|
||||
);
|
||||
enteImageCache.putThumbByID(asset.id, thumbBytes, key.smallThumbWidth);
|
||||
future.complete(thumbBytes);
|
||||
});
|
||||
thumbBytes = await future.future;
|
||||
enteImageCache.putThumbByID(asset.id, thumbBytes, key.smallThumbWidth);
|
||||
}
|
||||
if (thumbBytes != null) {
|
||||
@@ -71,10 +95,16 @@ class LocalThumbnailProvider extends ImageProvider<LocalThumbnailProviderKey> {
|
||||
}
|
||||
|
||||
if (normalThumbBytes == null) {
|
||||
normalThumbBytes = await asset.thumbnailDataWithSize(
|
||||
ThumbnailSize(key.width, key.height),
|
||||
quality: 50,
|
||||
);
|
||||
final Completer<Uint8List?> future = Completer();
|
||||
await mediumThumbnailQueue.addTask('${asset.id}-medium', () async {
|
||||
normalThumbBytes = await asset.thumbnailDataWithSize(
|
||||
ThumbnailSize(key.width, key.height),
|
||||
quality: 50,
|
||||
);
|
||||
enteImageCache.putThumbByID(asset.id, normalThumbBytes, key.height);
|
||||
future.complete(normalThumbBytes);
|
||||
});
|
||||
normalThumbBytes = await future.future;
|
||||
enteImageCache.putThumbByID(asset.id, normalThumbBytes, key.height);
|
||||
}
|
||||
if (normalThumbBytes == null) {
|
||||
@@ -82,7 +112,7 @@ class LocalThumbnailProvider extends ImageProvider<LocalThumbnailProviderKey> {
|
||||
"$runtimeType biThumb ${asset.title} failed",
|
||||
);
|
||||
}
|
||||
final buffer = await ui.ImmutableBuffer.fromUint8List(normalThumbBytes);
|
||||
final buffer = await ui.ImmutableBuffer.fromUint8List(normalThumbBytes!);
|
||||
final codec = await decode(buffer);
|
||||
yield codec;
|
||||
chunkEvents.close().ignore();
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
import "dart:typed_data";
|
||||
|
||||
import "package:photos/image/provider/local_thumbnail_img.dart";
|
||||
import "package:photos/utils/standalone/task_queue.dart";
|
||||
|
||||
class LocalThumbnailService {
|
||||
@@ -9,8 +6,4 @@ class LocalThumbnailService {
|
||||
taskTimeout: const Duration(minutes: 1),
|
||||
maxQueueSize: 100, // Limit the queue to 50 pending tasks
|
||||
);
|
||||
|
||||
Future<Uint8List?> _cached(LocalThumbnailProviderKey key) async {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,6 +71,7 @@ class _ThumbnailWidgetState extends State<ThumbnailWidget> {
|
||||
ImageProvider? _imageProvider;
|
||||
int? optimizedImageHeight;
|
||||
int? optimizedImageWidth;
|
||||
LocalThumbnailProviderKey? localImageProviderKey;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -81,7 +82,13 @@ class _ThumbnailWidgetState extends State<ThumbnailWidget> {
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
|
||||
Future.delayed(const Duration(milliseconds: 10), () {
|
||||
if (!mounted) {
|
||||
if (localImageProviderKey != null) {
|
||||
LocalThumbnailProvider.cancelRequest(localImageProviderKey!);
|
||||
}
|
||||
}
|
||||
// Cancel request only if the widget has been unmounted
|
||||
if (!mounted && widget.file.isRemoteFile && !_hasLoadedThumbnail) {
|
||||
removePendingGetThumbnailRequestIfAny(widget.file);
|
||||
@@ -125,13 +132,12 @@ class _ThumbnailWidgetState extends State<ThumbnailWidget> {
|
||||
).image;
|
||||
_hasLoadedThumbnail = true;
|
||||
} else {
|
||||
_imageProvider = LocalThumbnailProvider(
|
||||
LocalThumbnailProviderKey(
|
||||
asset: widget.file.asset!,
|
||||
height: widget.thumbnailSize,
|
||||
width: widget.thumbnailSize,
|
||||
),
|
||||
localImageProviderKey = LocalThumbnailProviderKey(
|
||||
asset: widget.file.asset!,
|
||||
height: widget.thumbnailSize,
|
||||
width: widget.thumbnailSize,
|
||||
);
|
||||
_imageProvider = LocalThumbnailProvider(localImageProviderKey!);
|
||||
}
|
||||
}
|
||||
Widget? image;
|
||||
|
||||
@@ -8,13 +8,16 @@ class _QueueItem<T> {
|
||||
final Future<void> Function() task;
|
||||
final Completer<void> completer;
|
||||
DateTime lastUpdated;
|
||||
int counter;
|
||||
|
||||
_QueueItem(this.id, this.task)
|
||||
: lastUpdated = DateTime.now(),
|
||||
counter = 1,
|
||||
completer = Completer<void>();
|
||||
|
||||
void updateTimestamp() {
|
||||
lastUpdated = DateTime.now();
|
||||
counter++;
|
||||
}
|
||||
|
||||
bool isTimedOut(Duration timeout) {
|
||||
@@ -148,8 +151,11 @@ class TaskQueue<T> {
|
||||
|
||||
if (_taskMap.containsKey(id)) {
|
||||
final item = _taskMap[id]!;
|
||||
item.counter--;
|
||||
if (item.counter > 0) {
|
||||
return false;
|
||||
}
|
||||
_priorityQueue.remove(item);
|
||||
|
||||
// Complete the future with a cancellation error
|
||||
if (!item.completer.isCompleted) {
|
||||
item.completer.completeError(Exception('Task $id was cancelled'));
|
||||
|
||||
Reference in New Issue
Block a user