image provider improvements

This commit is contained in:
mertalev 2025-08-13 00:10:55 -04:00
parent 0d60199514
commit f931060670
No known key found for this signature in database
GPG key ID: DF6ABC77AAD98C95
26 changed files with 1203 additions and 190 deletions

View file

@ -1,23 +1,21 @@
import 'dart:async';
import 'dart:ui';
import 'package:flutter/foundation.dart';
import 'package:flutter/painting.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:immich_mobile/domain/models/setting.model.dart';
import 'package:immich_mobile/domain/services/setting.service.dart';
import 'package:immich_mobile/extensions/codec_extensions.dart';
import 'package:immich_mobile/infrastructure/repositories/asset_media.repository.dart';
import 'package:immich_mobile/presentation/widgets/images/image_provider.dart';
import 'package:immich_mobile/presentation/widgets/images/one_frame_multi_image_stream_completer.dart';
import 'package:immich_mobile/providers/image/cache/image_loader.dart';
import 'package:immich_mobile/providers/image/cache/remote_image_cache_manager.dart';
import 'package:immich_mobile/services/api.service.dart';
import 'package:immich_mobile/utils/image_url_builder.dart';
class RemoteThumbProvider extends ImageProvider<RemoteThumbProvider> {
class RemoteThumbProvider extends ImageProvider<RemoteThumbProvider> with CancellableImageProviderMixin {
final String assetId;
final CacheManager? cacheManager;
const RemoteThumbProvider({required this.assetId, this.cacheManager});
RemoteThumbProvider({required this.assetId, this.cacheManager});
@override
Future<RemoteThumbProvider> obtainKey(ImageConfiguration configuration) {
@ -26,12 +24,8 @@ class RemoteThumbProvider extends ImageProvider<RemoteThumbProvider> {
@override
ImageStreamCompleter loadImage(RemoteThumbProvider key, ImageDecoderCallback decode) {
final cache = cacheManager ?? RemoteImageCacheManager();
final chunkController = StreamController<ImageChunkEvent>();
return MultiFrameImageStreamCompleter(
codec: _codec(key, cache, decode, chunkController),
scale: 1.0,
chunkEvents: chunkController.stream,
return OneFramePlaceholderImageStreamCompleter(
_codec(key, decode),
informationCollector: () => <DiagnosticsNode>[
DiagnosticsProperty<ImageProvider>('Image provider', this),
DiagnosticsProperty<String>('Asset Id', key.assetId),
@ -39,20 +33,17 @@ class RemoteThumbProvider extends ImageProvider<RemoteThumbProvider> {
);
}
Future<Codec> _codec(
RemoteThumbProvider key,
CacheManager cache,
ImageDecoderCallback decode,
StreamController<ImageChunkEvent> chunkController,
) async {
Stream<ImageInfo> _codec(RemoteThumbProvider key, ImageDecoderCallback decode) async* {
final preview = getThumbnailUrlForRemoteId(key.assetId);
return ImageLoader.loadImageFromCache(
preview,
cache: cache,
decode: decode,
chunkEvents: chunkController,
).whenComplete(chunkController.close);
final request = this.request = RemoteImageRequest(uri: preview, headers: ApiService.getRequestHeaders());
try {
final image = await request.load(decode);
if (image != null) {
yield image;
}
} finally {
this.request = null;
}
}
@override
@ -69,11 +60,11 @@ class RemoteThumbProvider extends ImageProvider<RemoteThumbProvider> {
int get hashCode => assetId.hashCode;
}
class RemoteFullImageProvider extends ImageProvider<RemoteFullImageProvider> {
class RemoteFullImageProvider extends ImageProvider<RemoteFullImageProvider> with CancellableImageProviderMixin {
final String assetId;
final CacheManager? cacheManager;
const RemoteFullImageProvider({required this.assetId, this.cacheManager});
RemoteFullImageProvider({required this.assetId, this.cacheManager});
@override
Future<RemoteFullImageProvider> obtainKey(ImageConfiguration configuration) {
@ -82,28 +73,44 @@ class RemoteFullImageProvider extends ImageProvider<RemoteFullImageProvider> {
@override
ImageStreamCompleter loadImage(RemoteFullImageProvider key, ImageDecoderCallback decode) {
final cache = cacheManager ?? RemoteImageCacheManager();
return OneFramePlaceholderImageStreamCompleter(
_codec(key, cache, decode),
initialImage: getCachedImage(RemoteThumbProvider(assetId: key.assetId)),
_codec(key, decode),
initialImage: getCachedImage(RemoteThumbProvider(assetId: assetId)),
informationCollector: () => <DiagnosticsNode>[
DiagnosticsProperty<ImageProvider>('Image provider', this),
DiagnosticsProperty<String>('Asset Id', key.assetId),
],
);
}
Stream<ImageInfo> _codec(RemoteFullImageProvider key, CacheManager cache, ImageDecoderCallback decode) async* {
final codec = await ImageLoader.loadImageFromCache(
getPreviewUrlForRemoteId(key.assetId),
cache: cache,
decode: decode,
);
yield await codec.getImageInfo();
Stream<ImageInfo> _codec(RemoteFullImageProvider key, ImageDecoderCallback decode) async* {
try {
final request = this.request = RemoteImageRequest(
uri: getPreviewUrlForRemoteId(key.assetId),
headers: ApiService.getRequestHeaders(),
);
final image = await request.load(decode);
if (image == null) {
return;
}
yield image;
} finally {
request = null;
}
if (AppSetting.get(Setting.loadOriginal)) {
final codec = await ImageLoader.loadImageFromCache(
getOriginalUrlForRemoteId(key.assetId),
cache: cache,
decode: decode,
);
yield await codec.getImageInfo();
try {
final request = this.request = RemoteImageRequest(
uri: getOriginalUrlForRemoteId(key.assetId),
headers: ApiService.getRequestHeaders(),
);
final image = await request.load(decode);
if (image != null) {
yield image;
}
} finally {
request = null;
}
}
}