mirror of
				https://gitlab.com/famedly/fluffychat.git
				synced 2025-11-04 06:17:26 +01:00 
			
		
		
		
	Merge branch 'soru/video-player' into 'main'
feat: Add video player Closes #192 See merge request famedly/fluffychat!479
This commit is contained in:
		
						commit
						e8376b56f1
					
				
							
								
								
									
										93
									
								
								lib/pages/video_viewer.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								lib/pages/video_viewer.dart
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,93 @@
 | 
			
		||||
import 'dart:io';
 | 
			
		||||
 | 
			
		||||
import 'package:matrix/matrix.dart';
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:vrouter/vrouter.dart';
 | 
			
		||||
import 'package:chewie/chewie.dart';
 | 
			
		||||
import 'package:video_player/video_player.dart';
 | 
			
		||||
import 'package:path_provider/path_provider.dart';
 | 
			
		||||
 | 
			
		||||
import 'views/video_viewer_view.dart';
 | 
			
		||||
import '../widgets/matrix.dart';
 | 
			
		||||
import '../utils/matrix_sdk_extensions.dart/event_extension.dart';
 | 
			
		||||
import '../utils/platform_infos.dart';
 | 
			
		||||
 | 
			
		||||
class VideoViewer extends StatefulWidget {
 | 
			
		||||
  final Event event;
 | 
			
		||||
 | 
			
		||||
  const VideoViewer(this.event, {Key key}) : super(key: key);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  VideoViewerController createState() => VideoViewerController();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class VideoViewerController extends State<VideoViewer> {
 | 
			
		||||
  VideoPlayerController videoPlayerController;
 | 
			
		||||
  ChewieController chewieController;
 | 
			
		||||
  dynamic error;
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  void initState() {
 | 
			
		||||
    super.initState();
 | 
			
		||||
    (() async {
 | 
			
		||||
      try {
 | 
			
		||||
        if (widget.event.content['file'] is Map) {
 | 
			
		||||
          if (PlatformInfos.isWeb) {
 | 
			
		||||
            throw 'Encrypted videos unavailable in web';
 | 
			
		||||
          }
 | 
			
		||||
          final tempDirectory = (await getTemporaryDirectory()).path;
 | 
			
		||||
          final mxcUri = widget.event.content
 | 
			
		||||
              .tryGet<Map<String, dynamic>>('file')
 | 
			
		||||
              ?.tryGet<String>('url');
 | 
			
		||||
          if (mxcUri == null) {
 | 
			
		||||
            throw 'No mxc uri found';
 | 
			
		||||
          }
 | 
			
		||||
          // somehow the video viewer doesn't like the uri-encoded slashes, so we'll just gonna replace them with hyphons
 | 
			
		||||
          final file = File(
 | 
			
		||||
              '$tempDirectory/videos/${mxcUri.replaceAll(':', '').replaceAll('/', '-')}');
 | 
			
		||||
          if (await file.exists() == false) {
 | 
			
		||||
            final matrixFile =
 | 
			
		||||
                await widget.event.downloadAndDecryptAttachmentCached();
 | 
			
		||||
            await file.create(recursive: true);
 | 
			
		||||
            await file.writeAsBytes(matrixFile.bytes);
 | 
			
		||||
          }
 | 
			
		||||
          videoPlayerController = VideoPlayerController.file(file);
 | 
			
		||||
        } else if (widget.event.content['url'] is String) {
 | 
			
		||||
          videoPlayerController = VideoPlayerController.network(
 | 
			
		||||
              widget.event.getAttachmentUrl()?.toString());
 | 
			
		||||
        } else {
 | 
			
		||||
          throw 'invalid event';
 | 
			
		||||
        }
 | 
			
		||||
        await videoPlayerController.initialize();
 | 
			
		||||
 | 
			
		||||
        chewieController = ChewieController(
 | 
			
		||||
          videoPlayerController: videoPlayerController,
 | 
			
		||||
          autoPlay: true,
 | 
			
		||||
          looping: false,
 | 
			
		||||
        );
 | 
			
		||||
        setState(() => null);
 | 
			
		||||
      } catch (e) {
 | 
			
		||||
        setState(() => error = e);
 | 
			
		||||
      }
 | 
			
		||||
    })();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  void dispose() {
 | 
			
		||||
    chewieController?.dispose();
 | 
			
		||||
    videoPlayerController?.dispose();
 | 
			
		||||
    super.dispose();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /// Forward this video to another room.
 | 
			
		||||
  void forwardAction() {
 | 
			
		||||
    Matrix.of(context).shareContent = widget.event.content;
 | 
			
		||||
    VRouter.of(context).to('/rooms');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /// Save this file with a system call.
 | 
			
		||||
  void saveFileAction() => widget.event.saveFile(context);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) => VideoViewerView(this);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										51
									
								
								lib/pages/views/video_viewer_view.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								lib/pages/views/video_viewer_view.dart
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,51 @@
 | 
			
		||||
import '../video_viewer.dart';
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
 | 
			
		||||
import 'package:chewie/chewie.dart';
 | 
			
		||||
 | 
			
		||||
class VideoViewerView extends StatelessWidget {
 | 
			
		||||
  final VideoViewerController controller;
 | 
			
		||||
 | 
			
		||||
  const VideoViewerView(this.controller, {Key key}) : super(key: key);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    return Scaffold(
 | 
			
		||||
      backgroundColor: Colors.black,
 | 
			
		||||
      extendBodyBehindAppBar: true,
 | 
			
		||||
      appBar: AppBar(
 | 
			
		||||
        elevation: 0,
 | 
			
		||||
        leading: IconButton(
 | 
			
		||||
          icon: Icon(Icons.close),
 | 
			
		||||
          onPressed: Navigator.of(context).pop,
 | 
			
		||||
          color: Colors.white,
 | 
			
		||||
          tooltip: L10n.of(context).close,
 | 
			
		||||
        ),
 | 
			
		||||
        backgroundColor: Color(0x44000000),
 | 
			
		||||
        actions: [
 | 
			
		||||
          IconButton(
 | 
			
		||||
            icon: Icon(Icons.reply_outlined),
 | 
			
		||||
            onPressed: controller.forwardAction,
 | 
			
		||||
            color: Colors.white,
 | 
			
		||||
            tooltip: L10n.of(context).share,
 | 
			
		||||
          ),
 | 
			
		||||
          IconButton(
 | 
			
		||||
            icon: Icon(Icons.download_outlined),
 | 
			
		||||
            onPressed: controller.saveFileAction,
 | 
			
		||||
            color: Colors.white,
 | 
			
		||||
            tooltip: L10n.of(context).downloadFile,
 | 
			
		||||
          ),
 | 
			
		||||
        ],
 | 
			
		||||
      ),
 | 
			
		||||
      body: Center(
 | 
			
		||||
        child: controller.error != null
 | 
			
		||||
            ? Text(controller.error.toString())
 | 
			
		||||
            : (controller.chewieController == null
 | 
			
		||||
                ? CircularProgressIndicator(strokeWidth: 2)
 | 
			
		||||
                : Chewie(
 | 
			
		||||
                    controller: controller.chewieController,
 | 
			
		||||
                  )),
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -38,7 +38,8 @@ extension LocalizedBody on Event {
 | 
			
		||||
      thumbnailInfoMap['size'] < room.client.database.maxFileSize;
 | 
			
		||||
 | 
			
		||||
  bool get showThumbnail =>
 | 
			
		||||
      [MessageTypes.Image, MessageTypes.Sticker].contains(messageType) &&
 | 
			
		||||
      [MessageTypes.Image, MessageTypes.Sticker, MessageTypes.Video]
 | 
			
		||||
          .contains(messageType) &&
 | 
			
		||||
      (kIsWeb ||
 | 
			
		||||
          isAttachmentSmallEnough ||
 | 
			
		||||
          isThumbnailSmallEnough ||
 | 
			
		||||
 | 
			
		||||
@ -250,8 +250,19 @@ class _ImageBubbleState extends State<ImageBubble> {
 | 
			
		||||
        _displayFile.bytes,
 | 
			
		||||
        key: ValueKey(key),
 | 
			
		||||
        fit: widget.fit,
 | 
			
		||||
        errorBuilder: (context, error, stacktrace) =>
 | 
			
		||||
            getErrorWidget(context, error),
 | 
			
		||||
        errorBuilder: (context, error, stacktrace) {
 | 
			
		||||
          if (widget.event.hasThumbnail && !_requestedThumbnailOnFailure) {
 | 
			
		||||
            _requestedThumbnailOnFailure = true;
 | 
			
		||||
            WidgetsBinding.instance.addPostFrameCallback((_) {
 | 
			
		||||
              setState(() {
 | 
			
		||||
                _file = null;
 | 
			
		||||
                _requestFile(getThumbnail: true);
 | 
			
		||||
              });
 | 
			
		||||
            });
 | 
			
		||||
            return getPlaceholderWidget();
 | 
			
		||||
          }
 | 
			
		||||
          return getErrorWidget(context, error);
 | 
			
		||||
        },
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -14,7 +14,9 @@ import 'package:matrix_link_text/link_text.dart';
 | 
			
		||||
import 'package:url_launcher/url_launcher.dart';
 | 
			
		||||
 | 
			
		||||
import '../../utils/url_launcher.dart';
 | 
			
		||||
import '../../utils/platform_infos.dart';
 | 
			
		||||
import '../../config/app_config.dart';
 | 
			
		||||
import '../../pages/video_viewer.dart';
 | 
			
		||||
import 'html_message.dart';
 | 
			
		||||
import '../matrix.dart';
 | 
			
		||||
import 'message_download_content.dart';
 | 
			
		||||
@ -121,6 +123,31 @@ class MessageContent extends StatelessWidget {
 | 
			
		||||
              color: textColor,
 | 
			
		||||
            );
 | 
			
		||||
          case MessageTypes.Video:
 | 
			
		||||
            if (event.showThumbnail &&
 | 
			
		||||
                (PlatformInfos.isMobile || PlatformInfos.isWeb)) {
 | 
			
		||||
              return InkWell(
 | 
			
		||||
                onTap: () => showDialog(
 | 
			
		||||
                  context: Matrix.of(context).navigatorContext,
 | 
			
		||||
                  useRootNavigator: false,
 | 
			
		||||
                  builder: (_) => VideoViewer(event),
 | 
			
		||||
                ),
 | 
			
		||||
                child: Stack(
 | 
			
		||||
                  alignment: Alignment.center,
 | 
			
		||||
                  children: <Widget>[
 | 
			
		||||
                    ImageBubble(
 | 
			
		||||
                      event,
 | 
			
		||||
                      width: 400,
 | 
			
		||||
                      height: 300,
 | 
			
		||||
                      fit: BoxFit.cover,
 | 
			
		||||
                      tapToView: false,
 | 
			
		||||
                    ),
 | 
			
		||||
                    Icon(Icons.play_circle_outline,
 | 
			
		||||
                        size: 200, color: Colors.grey),
 | 
			
		||||
                  ],
 | 
			
		||||
                ),
 | 
			
		||||
              );
 | 
			
		||||
            }
 | 
			
		||||
            return MessageDownloadContent(event, textColor);
 | 
			
		||||
          case MessageTypes.File:
 | 
			
		||||
            return MessageDownloadContent(event, textColor);
 | 
			
		||||
          case MessageTypes.Text:
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										65
									
								
								pubspec.lock
									
									
									
									
									
								
							
							
						
						
									
										65
									
								
								pubspec.lock
									
									
									
									
									
								
							@ -120,6 +120,13 @@ packages:
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "1.2.0"
 | 
			
		||||
  chewie:
 | 
			
		||||
    dependency: "direct main"
 | 
			
		||||
    description:
 | 
			
		||||
      name: chewie
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "1.2.2"
 | 
			
		||||
  cli_util:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
@ -182,7 +189,7 @@ packages:
 | 
			
		||||
      name: cupertino_icons
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "1.0.0"
 | 
			
		||||
    version: "1.0.3"
 | 
			
		||||
  dapackages:
 | 
			
		||||
    dependency: "direct dev"
 | 
			
		||||
    description:
 | 
			
		||||
@ -1460,6 +1467,27 @@ packages:
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "2.1.0"
 | 
			
		||||
  video_player:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: video_player
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "2.1.12"
 | 
			
		||||
  video_player_platform_interface:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: video_player_platform_interface
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "4.1.0"
 | 
			
		||||
  video_player_web:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: video_player_web
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "2.0.2"
 | 
			
		||||
  vm_service:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
@ -1474,6 +1502,41 @@ packages:
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "1.2.0+11"
 | 
			
		||||
  wakelock:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: wakelock
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "0.5.3+3"
 | 
			
		||||
  wakelock_macos:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: wakelock_macos
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "0.1.0+2"
 | 
			
		||||
  wakelock_platform_interface:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: wakelock_platform_interface
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "0.2.1+2"
 | 
			
		||||
  wakelock_web:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: wakelock_web
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "0.2.0+2"
 | 
			
		||||
  wakelock_windows:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: wakelock_windows
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "0.1.0+1"
 | 
			
		||||
  watcher:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
 | 
			
		||||
@ -11,6 +11,7 @@ dependencies:
 | 
			
		||||
  adaptive_theme: ^2.2.0
 | 
			
		||||
  audioplayers: ^0.19.1
 | 
			
		||||
  cached_network_image: ^3.1.0
 | 
			
		||||
  chewie: ^1.2.2
 | 
			
		||||
  cupertino_icons: any
 | 
			
		||||
  desktop_notifications: ">=0.4.0 <0.5.0" # Version 0.5.0 breaks web builds: https://github.com/canonical/dbus.dart/issues/250
 | 
			
		||||
  email_validator: ^2.0.1
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user