mirror of
				https://github.com/immich-app/immich.git
				synced 2025-11-04 03:27:09 -05:00 
			
		
		
		
	Adds photo thumbnail to videos (#2880)
* Motion photos use placeholder image for more seamless loading * Fixes merge conflicts
This commit is contained in:
		
							parent
							
								
									48e4ea5231
								
							
						
					
					
						commit
						1b15b5414c
					
				@ -367,6 +367,26 @@ class GalleryViewerPage extends HookConsumerWidget {
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ImageProvider imageProvider(Asset asset) {
 | 
			
		||||
      if (asset.isLocal) {
 | 
			
		||||
        return localImageProvider(asset);
 | 
			
		||||
      } else {
 | 
			
		||||
        if (isLoadOriginal.value) {
 | 
			
		||||
          return originalImageProvider(asset);
 | 
			
		||||
        } else if (isLoadPreview.value) {
 | 
			
		||||
          return remoteThumbnailImageProvider(
 | 
			
		||||
            asset,
 | 
			
		||||
            api.ThumbnailFormat.JPEG,
 | 
			
		||||
          );
 | 
			
		||||
        } else {
 | 
			
		||||
          return remoteThumbnailImageProvider(
 | 
			
		||||
            asset,
 | 
			
		||||
            api.ThumbnailFormat.WEBP,
 | 
			
		||||
          );
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return Scaffold(
 | 
			
		||||
      backgroundColor: Colors.black,
 | 
			
		||||
      body: WillPopScope(
 | 
			
		||||
@ -460,26 +480,9 @@ class GalleryViewerPage extends HookConsumerWidget {
 | 
			
		||||
                  : null,
 | 
			
		||||
              builder: (context, index) {
 | 
			
		||||
                final asset = loadAsset(index);
 | 
			
		||||
                final ImageProvider provider = imageProvider(asset);
 | 
			
		||||
 | 
			
		||||
                if (asset.isImage && !isPlayingMotionVideo.value) {
 | 
			
		||||
                  // Show photo
 | 
			
		||||
                  final ImageProvider provider;
 | 
			
		||||
                  if (asset.isLocal) {
 | 
			
		||||
                    provider = localImageProvider(asset);
 | 
			
		||||
                  } else {
 | 
			
		||||
                    if (isLoadOriginal.value) {
 | 
			
		||||
                      provider = originalImageProvider(asset);
 | 
			
		||||
                    } else if (isLoadPreview.value) {
 | 
			
		||||
                      provider = remoteThumbnailImageProvider(
 | 
			
		||||
                        asset,
 | 
			
		||||
                        api.ThumbnailFormat.JPEG,
 | 
			
		||||
                      );
 | 
			
		||||
                    } else {
 | 
			
		||||
                      provider = remoteThumbnailImageProvider(
 | 
			
		||||
                        asset,
 | 
			
		||||
                        api.ThumbnailFormat.WEBP,
 | 
			
		||||
                      );
 | 
			
		||||
                    }
 | 
			
		||||
                  }
 | 
			
		||||
                  return PhotoViewGalleryPageOptions(
 | 
			
		||||
                    onDragStart: (_, details, __) =>
 | 
			
		||||
                        localPosition = details.localPosition,
 | 
			
		||||
@ -512,19 +515,24 @@ class GalleryViewerPage extends HookConsumerWidget {
 | 
			
		||||
                    maxScale: 1.0,
 | 
			
		||||
                    minScale: 1.0,
 | 
			
		||||
                    basePosition: Alignment.bottomCenter,
 | 
			
		||||
                    child: SafeArea(
 | 
			
		||||
                    child: VideoViewerPage(
 | 
			
		||||
                      onPlaying: () => isPlayingVideo.value = true,
 | 
			
		||||
                      onPaused: () => isPlayingVideo.value = false,
 | 
			
		||||
                      asset: asset,
 | 
			
		||||
                      isMotionVideo: isPlayingMotionVideo.value,
 | 
			
		||||
                      placeholder: Image(
 | 
			
		||||
                        image: provider,
 | 
			
		||||
                        fit: BoxFit.fitWidth,
 | 
			
		||||
                        height: MediaQuery.of(context).size.height,
 | 
			
		||||
                        width: MediaQuery.of(context).size.width,
 | 
			
		||||
                        alignment: Alignment.center,
 | 
			
		||||
                      ),
 | 
			
		||||
                      onVideoEnded: () {
 | 
			
		||||
                        if (isPlayingMotionVideo.value) {
 | 
			
		||||
                          isPlayingMotionVideo.value = false;
 | 
			
		||||
                        }
 | 
			
		||||
                      },
 | 
			
		||||
                    ),
 | 
			
		||||
                    ),
 | 
			
		||||
                  );
 | 
			
		||||
                }
 | 
			
		||||
              },
 | 
			
		||||
 | 
			
		||||
@ -15,6 +15,7 @@ import 'package:video_player/video_player.dart';
 | 
			
		||||
class VideoViewerPage extends HookConsumerWidget {
 | 
			
		||||
  final Asset asset;
 | 
			
		||||
  final bool isMotionVideo;
 | 
			
		||||
  final Widget? placeholder;
 | 
			
		||||
  final VoidCallback onVideoEnded;
 | 
			
		||||
  final VoidCallback? onPlaying;
 | 
			
		||||
  final VoidCallback? onPaused;
 | 
			
		||||
@ -26,6 +27,7 @@ class VideoViewerPage extends HookConsumerWidget {
 | 
			
		||||
    required this.onVideoEnded,
 | 
			
		||||
    this.onPlaying,
 | 
			
		||||
    this.onPaused,
 | 
			
		||||
    this.placeholder,
 | 
			
		||||
  }) : super(key: key);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
@ -66,6 +68,7 @@ class VideoViewerPage extends HookConsumerWidget {
 | 
			
		||||
          onVideoEnded: onVideoEnded,
 | 
			
		||||
          onPaused: onPaused,
 | 
			
		||||
          onPlaying: onPlaying,
 | 
			
		||||
          placeholder: placeholder,
 | 
			
		||||
        ),
 | 
			
		||||
        if (downloadAssetStatus == DownloadAssetStatus.loading)
 | 
			
		||||
          const Center(
 | 
			
		||||
@ -95,6 +98,10 @@ class VideoPlayer extends StatefulWidget {
 | 
			
		||||
  final Function()? onPlaying;
 | 
			
		||||
  final Function()? onPaused;
 | 
			
		||||
 | 
			
		||||
  /// The placeholder to show while the video is loading
 | 
			
		||||
  /// usually, a thumbnail of the video
 | 
			
		||||
  final Widget? placeholder;
 | 
			
		||||
 | 
			
		||||
  const VideoPlayer({
 | 
			
		||||
    Key? key,
 | 
			
		||||
    this.url,
 | 
			
		||||
@ -104,6 +111,7 @@ class VideoPlayer extends StatefulWidget {
 | 
			
		||||
    required this.isMotionVideo,
 | 
			
		||||
    this.onPlaying,
 | 
			
		||||
    this.onPaused,
 | 
			
		||||
    this.placeholder,
 | 
			
		||||
  }) : super(key: key);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
@ -186,12 +194,18 @@ class _VideoPlayerState extends State<VideoPlayer> {
 | 
			
		||||
        ),
 | 
			
		||||
      );
 | 
			
		||||
    } else {
 | 
			
		||||
      return const Center(
 | 
			
		||||
        child: SizedBox(
 | 
			
		||||
          width: 75,
 | 
			
		||||
          height: 75,
 | 
			
		||||
          child: CircularProgressIndicator.adaptive(
 | 
			
		||||
            strokeWidth: 2,
 | 
			
		||||
      return SizedBox(
 | 
			
		||||
        height: MediaQuery.of(context).size.height,
 | 
			
		||||
        width: MediaQuery.of(context).size.width,
 | 
			
		||||
        child: Center(
 | 
			
		||||
          child: Stack(
 | 
			
		||||
            children: [
 | 
			
		||||
              if (widget.placeholder != null)
 | 
			
		||||
                widget.placeholder!,
 | 
			
		||||
              const Center(
 | 
			
		||||
                child: ImmichLoadingIndicator(),
 | 
			
		||||
              ),
 | 
			
		||||
            ],
 | 
			
		||||
          ),
 | 
			
		||||
        ),
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user