From 5be9c47f2389a012517deb80e872321891f96018 Mon Sep 17 00:00:00 2001 From: ArThirtyFour <149522975+ArThirtyFour@users.noreply.github.com> Date: Wed, 15 Jul 2026 21:04:09 +0300 Subject: [PATCH 1/2] Add video player for Desktop + Copy image --- lib/main.dart | 3 + lib/pages/image_viewer/image_viewer.dart | 9 +- lib/pages/image_viewer/image_viewer_view.dart | 73 ++++++- lib/pages/image_viewer/video_player.dart | 193 +++++++++++++----- lib/utils/clipboard_utils.dart | 21 ++ lib/utils/platform_infos.dart | 2 +- linux/flutter/generated_plugin_registrant.cc | 12 ++ linux/flutter/generated_plugins.cmake | 3 + pubspec.lock | 100 ++++++++- pubspec.yaml | 7 + windows/flutter/CMakeLists.txt | 7 +- .../flutter/generated_plugin_registrant.cc | 9 + windows/flutter/generated_plugins.cmake | 3 + 13 files changed, 375 insertions(+), 67 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 69fdff1bf..d25c8298d 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -10,6 +10,7 @@ import 'package:flutter_vodozemac/flutter_vodozemac.dart' as vod; import 'package:matrix/matrix.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:timezone/data/latest.dart' as tz; +import 'package:media_kit/media_kit.dart'; import 'package:extera_next/config/app_config.dart'; import 'package:extera_next/utils/client_manager.dart'; @@ -40,6 +41,8 @@ void main() async { // widget bindings are initialized already. WidgetsFlutterBinding.ensureInitialized(); + MediaKit.ensureInitialized(); + tz.initializeTimeZones(); FlutterForegroundTask.initCommunicationPort(); diff --git a/lib/pages/image_viewer/image_viewer.dart b/lib/pages/image_viewer/image_viewer.dart index 1c12601be..881df983d 100644 --- a/lib/pages/image_viewer/image_viewer.dart +++ b/lib/pages/image_viewer/image_viewer.dart @@ -38,7 +38,7 @@ class ImageViewerController extends State { (event) => { MessageTypes.Image, MessageTypes.Sticker, - if (PlatformInfos.supportsVideoPlayer) MessageTypes.Video, + MessageTypes.Video, }.contains(event.messageType), ) .toList() @@ -118,13 +118,6 @@ class ImageViewerController extends State { } } - @override - void dispose() { - focusNode.dispose(); - pageController.dispose(); - super.dispose(); - } - @override Widget build(BuildContext context) => ImageViewerView(this); } diff --git a/lib/pages/image_viewer/image_viewer_view.dart b/lib/pages/image_viewer/image_viewer_view.dart index 72e07d098..717e9d707 100644 --- a/lib/pages/image_viewer/image_viewer_view.dart +++ b/lib/pages/image_viewer/image_viewer_view.dart @@ -4,6 +4,8 @@ import 'package:matrix/matrix.dart'; import 'package:extera_next/generated/l10n/l10n.dart'; import 'package:extera_next/pages/image_viewer/video_player.dart'; +import 'package:extera_next/utils/clipboard_utils.dart'; +import 'package:extera_next/utils/matrix_sdk_extensions/event_extension.dart'; import 'package:extera_next/utils/platform_infos.dart'; import 'package:extera_next/widgets/hover_builder.dart'; import 'package:extera_next/widgets/mxc_image.dart'; @@ -195,21 +197,83 @@ class _ZoomableImageState extends State<_ZoomableImage> { super.dispose(); } + void _onSecondaryTapUp(TapUpDetails details) { + if (!PlatformInfos.isDesktop) return; + final overlay = + Overlay.of(context).context.findRenderObject() as RenderBox; + showMenu( + context: context, + position: RelativeRect.fromLTRB( + details.globalPosition.dx, + details.globalPosition.dy, + overlay.size.width - details.globalPosition.dx, + overlay.size.height - details.globalPosition.dy, + ), + items: [ + PopupMenuItem( + value: 0, + child: Row( + children: [ + const Icon(Icons.copy_outlined, size: 20), + const SizedBox(width: 8), + Text(L10n.of(context).copy), + ], + ), + ), + PopupMenuItem( + value: 1, + child: Row( + children: [ + const Icon(Icons.save_alt_outlined, size: 20), + const SizedBox(width: 8), + Text(L10n.of(context).downloadFile), + ], + ), + ), + ], + ).then((value) { + if (value == 0) { + _copyImageToClipboard(); + } else if (value == 1) { + widget.event.saveFile(context); + } + }); + } + + Future _copyImageToClipboard() async { + try { + final data = await widget.event.downloadAndDecryptAttachment( + getThumbnail: false, + ); + await writeImageToClipboard(data.bytes); + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(L10n.of(context).copiedToClipboard), + duration: const Duration(seconds: 1), + ), + ); + } + } catch (e) { + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Failed to copy image: $e')), + ); + } + } + } + @override Widget build(BuildContext context) { return InteractiveViewer( transformationController: _transformController, minScale: 1.0, maxScale: 10.0, - // When the user puts fingers on screen, disable PageView scroll onInteractionStart: (_) { widget.onZoomStatusChanged(true); }, - // When interaction ends, check if we are still zoomed in onInteractionEnd: (details) { widget.controller.onInteractionEnds(details); - - // Identity matrix means scale is 1.0 and offset is 0,0 final isZoomed = _transformController.value.row0[0] != 1.0; if (!isZoomed) { widget.onZoomStatusChanged(false); @@ -220,6 +284,7 @@ class _ZoomableImageState extends State<_ZoomableImage> { tag: widget.event.eventId, child: GestureDetector( onTap: () {}, + onSecondaryTapUp: _onSecondaryTapUp, child: MxcImage( key: ValueKey(widget.event.eventId), event: widget.event, diff --git a/lib/pages/image_viewer/video_player.dart b/lib/pages/image_viewer/video_player.dart index 74674dedc..322a1b4c8 100644 --- a/lib/pages/image_viewer/video_player.dart +++ b/lib/pages/image_viewer/video_player.dart @@ -8,10 +8,11 @@ import 'package:matrix/matrix.dart'; import 'package:path_provider/path_provider.dart'; import 'package:universal_html/html.dart' as html; import 'package:video_player/video_player.dart'; +import 'package:media_kit/media_kit.dart'; +import 'package:media_kit_video/media_kit_video.dart'; import 'package:extera_next/pages/image_viewer/image_viewer.dart'; import 'package:extera_next/utils/localized_exception_extension.dart'; -import 'package:extera_next/utils/matrix_sdk_extensions/event_extension.dart'; import 'package:extera_next/utils/platform_infos.dart'; import 'package:extera_next/widgets/blur_hash.dart'; import '../../../utils/error_reporter.dart'; @@ -31,20 +32,92 @@ class EventVideoPlayerState extends State { ChewieController? _chewieController; VideoPlayerController? _videoPlayerController; + Player? _mediaKitPlayer; + VideoController? _mediaKitController; + double? _downloadProgress; - // The video_player package only doesn't support Windows and Linux. - final _supportsVideoPlayer = - !PlatformInfos.isWindows && !PlatformInfos.isLinux; + bool get _useMediaKit => + !kIsWeb && (PlatformInfos.isWindows || PlatformInfos.isLinux); void _downloadAction() async { - if (!_supportsVideoPlayer) { - widget.event.saveFile(context); - return; + if (_useMediaKit) { + _downloadActionMediaKit(); + } else { + _downloadActionVideoPlayer(); } + } + void _downloadActionMediaKit() async { + try { + _disposeControllers(); + final player = Player(); + _mediaKitPlayer = player; + _mediaKitController = VideoController(player); + + if (widget.event.room.encrypted) { + final fileSize = widget.event.content + .tryGetMap('info') + ?.tryGet('size'); + + final videoFile = await widget.event.downloadAndDecryptAttachment( + onDownloadProgress: fileSize == null + ? null + : (progress) { + final progressPercentage = progress / fileSize; + setState(() { + _downloadProgress = progressPercentage < 1 + ? progressPercentage + : null; + }); + }, + ); + + final tempDir = await getTemporaryDirectory(); + final fileName = Uri.encodeComponent( + widget.event.attachmentOrThumbnailMxcUrl()!.pathSegments.last, + ); + final file = File('${tempDir.path}/${fileName}_${videoFile.name}'); + if (!await file.exists()) { + await file.writeAsBytes(videoFile.bytes); + } + await player.open(Media(file.path)); + } else { + final videoUrl = await widget.event.attachmentMxcUrl!.getDownloadUri( + widget.event.room.client, + ); + Logs().d("Video url: $videoUrl"); + await player.open( + Media( + videoUrl.toString(), + httpHeaders: { + 'authorization': 'Bearer ${widget.event.room.client.accessToken}', + }, + ), + ); + } + + if (widget.ivController.currentEvent.eventId != widget.event.eventId) { + dispose(); + return; + } + + setState(() {}); + } on IOException catch (e) { + if (mounted) { + ScaffoldMessenger.of( + context, + ).showSnackBar(SnackBar(content: Text(e.toLocalizedString(context)))); + } + } catch (e, s) { + if (mounted) { + ErrorReporter(context, 'Unable to play video').onErrorCallback(e, s); + } + } + } + + void _downloadActionVideoPlayer() async { try { - // Dispose the controllers if we already have them. _disposeControllers(); late VideoPlayerController videoPlayerController; @@ -65,7 +138,6 @@ class EventVideoPlayerState extends State { }); }, ); - // Create the VideoPlayerController from the contents of videoFile. if (kIsWeb) { final blob = html.Blob([videoFile.bytes]); final networkUri = Uri.parse(html.Url.createObjectUrlFromBlob(blob)); @@ -103,7 +175,6 @@ class EventVideoPlayerState extends State { return; } - // Create a ChewieController on top. setState(() { _chewieController = ChewieController( videoPlayerController: videoPlayerController, @@ -127,6 +198,9 @@ class EventVideoPlayerState extends State { _videoPlayerController?.dispose(); _chewieController = null; _videoPlayerController = null; + _mediaKitPlayer?.dispose(); + _mediaKitPlayer = null; + _mediaKitController = null; } @override @@ -160,46 +234,63 @@ class EventVideoPlayerState extends State { final width = videoWidth * (height / videoHeight); final chewieController = _chewieController; - return chewieController != null - ? Center( - child: SizedBox( - width: width, - height: height, - child: Chewie(controller: chewieController), - ), - ) - : Stack( - children: [ - Center( - child: Hero( - tag: widget.event.eventId, - child: hasThumbnail - ? MxcImage( - event: widget.event, - isThumbnail: true, - width: width, - height: height, - fit: BoxFit.cover, - placeholder: (context) => BlurHash( - blurhash: blurHash, - width: width, - height: height, - fit: BoxFit.cover, - ), - ) - : BlurHash( - blurhash: blurHash, - width: width, - height: height, - ), - ), - ), - Center( - child: CircularProgressIndicator.adaptive( - value: _downloadProgress, - ), - ), - ], - ); + final mediaKitController = _mediaKitController; + + if (chewieController != null) { + return Center( + child: SizedBox( + width: width, + height: height, + child: Chewie(controller: chewieController), + ), + ); + } + + if (mediaKitController != null) { + return Center( + child: SizedBox( + width: width, + height: height, + child: Video( + controller: mediaKitController, + controls: MaterialVideoControls, + ), + ), + ); + } + + return Stack( + children: [ + Center( + child: Hero( + tag: widget.event.eventId, + child: hasThumbnail + ? MxcImage( + event: widget.event, + isThumbnail: true, + width: width, + height: height, + fit: BoxFit.cover, + placeholder: (context) => BlurHash( + blurhash: blurHash, + width: width, + height: height, + fit: BoxFit.cover, + ), + ) + : BlurHash( + blurhash: blurHash, + width: width, + height: height, + ), + ), + ), + Center( + child: CircularProgressIndicator.adaptive( + value: _downloadProgress, + ), + ), + ], + ); } } diff --git a/lib/utils/clipboard_utils.dart b/lib/utils/clipboard_utils.dart index f9728abe1..d493cf402 100644 --- a/lib/utils/clipboard_utils.dart +++ b/lib/utils/clipboard_utils.dart @@ -3,6 +3,27 @@ import 'dart:typed_data'; import 'package:pasteboard/pasteboard.dart'; +Future writeImageToClipboard(Uint8List bytes) async { + if (Platform.isWindows || Platform.isIOS || Platform.isMacOS || Platform.isAndroid) { + await Pasteboard.writeImage(bytes); + } else if (Platform.isLinux) { + final tempFile = File('${Directory.systemTemp.path}/clipboard_image.png'); + await tempFile.writeAsBytes(bytes); + try { + await Process.run('xclip', [ + '-selection', 'clipboard', + '-t', 'image/png', + '-i', tempFile.path, + ]); + } catch (_) { + try { + await Process.run('wl-copy', ['--type', 'image/png', tempFile.path]); + } catch (_) {} + } + await tempFile.delete(); + } +} + Future getImageFromClipboardLinux() async { final cmds = [ ['wl-paste', '-t', 'image/png'], diff --git a/lib/utils/platform_infos.dart b/lib/utils/platform_infos.dart index a56808357..dc6fa62b3 100644 --- a/lib/utils/platform_infos.dart +++ b/lib/utils/platform_infos.dart @@ -30,7 +30,7 @@ abstract class PlatformInfos { static bool get usesTouchscreen => !isMobile; - static bool get supportsVideoPlayer => !isWindows && !isLinux; + static bool get supportsVideoPlayer => true; /// Web could also record in theory but currently only wav which is too large static bool get platformCanRecord => (isMobile || isMacOS); diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index a236cb121..0e9afd084 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -17,12 +17,15 @@ #include #include #include +#include +#include #include #include #include #include #include #include +#include #include #include #include @@ -61,6 +64,12 @@ void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) livekit_client_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "LiveKitPlugin"); live_kit_plugin_register_with_registrar(livekit_client_registrar); + g_autoptr(FlPluginRegistrar) media_kit_libs_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "MediaKitLibsLinuxPlugin"); + media_kit_libs_linux_plugin_register_with_registrar(media_kit_libs_linux_registrar); + g_autoptr(FlPluginRegistrar) media_kit_video_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "MediaKitVideoPlugin"); + media_kit_video_plugin_register_with_registrar(media_kit_video_registrar); g_autoptr(FlPluginRegistrar) open_file_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "OpenFileLinuxPlugin"); open_file_linux_plugin_register_with_registrar(open_file_linux_registrar); @@ -79,6 +88,9 @@ void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); + g_autoptr(FlPluginRegistrar) volume_controller_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "VolumeControllerPlugin"); + volume_controller_plugin_register_with_registrar(volume_controller_registrar); g_autoptr(FlPluginRegistrar) webcrypto_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "WebcryptoPlugin"); webcrypto_plugin_register_with_registrar(webcrypto_registrar); diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index 35928031a..4343c64ec 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -14,12 +14,15 @@ list(APPEND FLUTTER_PLUGIN_LIST handy_window hotkey_manager_linux livekit_client + media_kit_libs_linux + media_kit_video open_file_linux pasteboard record_linux screen_retriever_linux sqlcipher_flutter_libs url_launcher_linux + volume_controller webcrypto window_manager window_to_front diff --git a/pubspec.lock b/pubspec.lock index 6ab772312..5cedc482e 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1438,6 +1438,62 @@ packages: relative: true source: path version: "7.3.0" + media_kit: + dependency: "direct main" + description: + name: media_kit + sha256: ae9e79597500c7ad6083a3c7b7b7544ddabfceacce7ae5c9709b0ec16a5d6643 + url: "https://pub.dev" + source: hosted + version: "1.2.6" + media_kit_libs_android_video: + dependency: "direct main" + description: + name: media_kit_libs_android_video + sha256: "3f6274e5ab2de512c286a25c327288601ee445ed8ac319e0ef0b66148bd8f76c" + url: "https://pub.dev" + source: hosted + version: "1.3.8" + media_kit_libs_ios_video: + dependency: "direct main" + description: + name: media_kit_libs_ios_video + sha256: b5382994eb37a4564c368386c154ad70ba0cc78dacdd3fb0cd9f30db6d837991 + url: "https://pub.dev" + source: hosted + version: "1.1.4" + media_kit_libs_linux: + dependency: "direct main" + description: + name: media_kit_libs_linux + sha256: "2b473399a49ec94452c4d4ae51cfc0f6585074398d74216092bf3d54aac37ecf" + url: "https://pub.dev" + source: hosted + version: "1.2.1" + media_kit_libs_macos_video: + dependency: "direct main" + description: + name: media_kit_libs_macos_video + sha256: f26aa1452b665df288e360393758f84b911f70ffb3878032e1aabba23aa1032d + url: "https://pub.dev" + source: hosted + version: "1.1.4" + media_kit_libs_windows_video: + dependency: "direct main" + description: + name: media_kit_libs_windows_video + sha256: dff76da2778729ab650229e6b4ec6ec111eb5151431002cbd7ea304ff1f112ab + url: "https://pub.dev" + source: hosted + version: "1.0.11" + media_kit_video: + dependency: "direct main" + description: + name: media_kit_video + sha256: "813858c3fe84eb46679eb698695f60665e2bfbef757766fac4d2e683f926e15a" + url: "https://pub.dev" + source: hosted + version: "1.3.1" meta: dependency: transitive description: @@ -2014,6 +2070,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.28.0" + safe_local_storage: + dependency: transitive + description: + name: safe_local_storage + sha256: "7483b3d5e8976f0bd263647c03b96131ee8e43f48b56fa8a8ec459e8515d74b0" + url: "https://pub.dev" + source: hosted + version: "2.0.4" safe_url_check: dependency: transitive description: @@ -2022,6 +2086,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.2" + screen_brightness_android: + dependency: transitive + description: + name: screen_brightness_android + sha256: "2008ad8e9527cc968f7a4de1ec58b476d495b3c612a149dbd6550c4f046da147" + url: "https://pub.dev" + source: hosted + version: "2.1.6" + screen_brightness_platform_interface: + dependency: transitive + description: + name: screen_brightness_platform_interface + sha256: "2de60c0ba569b898950029cc1f7e9dd72bda44a22beb5054aac331cb6fce2ff2" + url: "https://pub.dev" + source: hosted + version: "2.1.2" screen_retriever: dependency: transitive description: @@ -2507,6 +2587,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.3.2" + uri_parser: + dependency: transitive + description: + name: uri_parser + sha256: "051c62e5f693de98ca9f130ee707f8916e2266945565926be3ff20659f7853ce" + url: "https://pub.dev" + source: hosted + version: "3.0.2" url_launcher: dependency: "direct main" description: @@ -2675,6 +2763,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.5.0" + volume_controller: + dependency: transitive + description: + name: volume_controller + sha256: "851b43150972f96fa04a16af1080be9cabcd740c1b7e91dc3f5be9461ff45010" + url: "https://pub.dev" + source: hosted + version: "3.6.0" wakelock_plus: dependency: "direct main" description: @@ -2852,5 +2948,5 @@ packages: source: hosted version: "3.1.3" sdks: - dart: ">=3.11.0 <4.0.0" - flutter: ">=3.41.0" + dart: ">=3.12.0 <4.0.0" + flutter: ">=3.44.0" diff --git a/pubspec.yaml b/pubspec.yaml index 5eaf76521..faf4ca120 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -103,6 +103,13 @@ dependencies: url_launcher: ^6.3.2 video_compress: ^3.1.4 video_player: ^2.11.1 + media_kit: ^1.1.11 + media_kit_video: ^1.2.5 + media_kit_libs_android_video: ^1.3.6 + media_kit_libs_ios_video: ^1.1.4 + media_kit_libs_macos_video: ^1.1.4 + media_kit_libs_windows_video: ^1.0.9 + media_kit_libs_linux: ^1.1.3 vodozemac: ^0.5.0 wakelock_plus: ^1.4.0 webrtc_interface: ^1.4.0 diff --git a/windows/flutter/CMakeLists.txt b/windows/flutter/CMakeLists.txt index 930d2071a..903f4899d 100644 --- a/windows/flutter/CMakeLists.txt +++ b/windows/flutter/CMakeLists.txt @@ -10,6 +10,11 @@ include(${EPHEMERAL_DIR}/generated_config.cmake) # https://github.com/flutter/flutter/issues/57146. set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") +# Set fallback configurations for older versions of the flutter tool. +if (NOT DEFINED FLUTTER_TARGET_PLATFORM) + set(FLUTTER_TARGET_PLATFORM "windows-x64") +endif() + # === Flutter Library === set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") @@ -92,7 +97,7 @@ add_custom_command( COMMAND ${CMAKE_COMMAND} -E env ${FLUTTER_TOOL_ENVIRONMENT} "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" - windows-x64 $ + ${FLUTTER_TARGET_PLATFORM} $ VERBATIM ) add_custom_target(flutter_assemble DEPENDS diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index b67a3aab2..3c71c0712 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -18,6 +18,8 @@ #include #include #include +#include +#include #include #include #include @@ -25,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -54,6 +57,10 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { registry->GetRegistrarForPlugin("HotkeyManagerWindowsPluginCApi")); LiveKitPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("LiveKitPlugin")); + MediaKitLibsWindowsVideoPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("MediaKitLibsWindowsVideoPluginCApi")); + MediaKitVideoPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("MediaKitVideoPluginCApi")); PasteboardPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("PasteboardPlugin")); PermissionHandlerWindowsPluginRegisterWithRegistrar( @@ -68,6 +75,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { registry->GetRegistrarForPlugin("Sqlite3FlutterLibsPlugin")); UrlLauncherWindowsRegisterWithRegistrar( registry->GetRegistrarForPlugin("UrlLauncherWindows")); + VolumeControllerPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("VolumeControllerPluginCApi")); WebcryptoPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("WebcryptoPlugin")); WindowManagerPluginRegisterWithRegistrar( diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index b4362e475..57d1dbd7f 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -15,6 +15,8 @@ list(APPEND FLUTTER_PLUGIN_LIST geolocator_windows hotkey_manager_windows livekit_client + media_kit_libs_windows_video + media_kit_video pasteboard permission_handler_windows record_windows @@ -22,6 +24,7 @@ list(APPEND FLUTTER_PLUGIN_LIST share_plus sqlcipher_flutter_libs url_launcher_windows + volume_controller webcrypto window_manager window_to_front From 2f8a28edb362a0cc83c0438f5f82f9dd7c7950be Mon Sep 17 00:00:00 2001 From: ArThirtyFour <149522975+ArThirtyFour@users.noreply.github.com> Date: Wed, 15 Jul 2026 23:50:24 +0300 Subject: [PATCH 2/2] Fix kakish --- lib/pages/image_viewer/image_viewer.dart | 7 ++ lib/pages/image_viewer/video_player.dart | 109 ------------------ linux/flutter/generated_plugin_registrant.cc | 4 - linux/flutter/generated_plugins.cmake | 1 - pubspec.lock | 98 +++------------- pubspec.yaml | 12 +- .../flutter/generated_plugin_registrant.cc | 3 - windows/flutter/generated_plugins.cmake | 1 - 8 files changed, 27 insertions(+), 208 deletions(-) diff --git a/lib/pages/image_viewer/image_viewer.dart b/lib/pages/image_viewer/image_viewer.dart index 881df983d..05ec9f529 100644 --- a/lib/pages/image_viewer/image_viewer.dart +++ b/lib/pages/image_viewer/image_viewer.dart @@ -118,6 +118,13 @@ class ImageViewerController extends State { } } + @override + void dispose() { + focusNode.dispose(); + pageController.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) => ImageViewerView(this); } diff --git a/lib/pages/image_viewer/video_player.dart b/lib/pages/image_viewer/video_player.dart index 322a1b4c8..7c1c915e1 100644 --- a/lib/pages/image_viewer/video_player.dart +++ b/lib/pages/image_viewer/video_player.dart @@ -3,11 +3,8 @@ import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:chewie/chewie.dart'; import 'package:matrix/matrix.dart'; import 'package:path_provider/path_provider.dart'; -import 'package:universal_html/html.dart' as html; -import 'package:video_player/video_player.dart'; import 'package:media_kit/media_kit.dart'; import 'package:media_kit_video/media_kit_video.dart'; @@ -29,26 +26,12 @@ class EventVideoPlayer extends StatefulWidget { } class EventVideoPlayerState extends State { - ChewieController? _chewieController; - VideoPlayerController? _videoPlayerController; - Player? _mediaKitPlayer; VideoController? _mediaKitController; double? _downloadProgress; - bool get _useMediaKit => - !kIsWeb && (PlatformInfos.isWindows || PlatformInfos.isLinux); - void _downloadAction() async { - if (_useMediaKit) { - _downloadActionMediaKit(); - } else { - _downloadActionVideoPlayer(); - } - } - - void _downloadActionMediaKit() async { try { _disposeControllers(); final player = Player(); @@ -116,88 +99,7 @@ class EventVideoPlayerState extends State { } } - void _downloadActionVideoPlayer() async { - try { - _disposeControllers(); - late VideoPlayerController videoPlayerController; - - if (widget.event.room.encrypted) { - final fileSize = widget.event.content - .tryGetMap('info') - ?.tryGet('size'); - - final videoFile = await widget.event.downloadAndDecryptAttachment( - onDownloadProgress: fileSize == null - ? null - : (progress) { - final progressPercentage = progress / fileSize; - setState(() { - _downloadProgress = progressPercentage < 1 - ? progressPercentage - : null; - }); - }, - ); - if (kIsWeb) { - final blob = html.Blob([videoFile.bytes]); - final networkUri = Uri.parse(html.Url.createObjectUrlFromBlob(blob)); - videoPlayerController = VideoPlayerController.networkUrl(networkUri); - } else { - final tempDir = await getTemporaryDirectory(); - final fileName = Uri.encodeComponent( - widget.event.attachmentOrThumbnailMxcUrl()!.pathSegments.last, - ); - final file = File('${tempDir.path}/${fileName}_${videoFile.name}'); - if (await file.exists() == false) { - await file.writeAsBytes(videoFile.bytes); - } - videoPlayerController = VideoPlayerController.file(file); - } - } else { - final videoUrl = await widget.event.attachmentMxcUrl!.getDownloadUri( - widget.event.room.client, - ); - Logs().d("Video url: $videoUrl"); - videoPlayerController = VideoPlayerController.networkUrl( - videoUrl, - httpHeaders: { - 'authorization': 'Bearer ${widget.event.room.client.accessToken}', - }, - ); - } - - _videoPlayerController = videoPlayerController; - - await videoPlayerController.initialize(); - - if (widget.ivController.currentEvent.eventId != widget.event.eventId) { - dispose(); - return; - } - - setState(() { - _chewieController = ChewieController( - videoPlayerController: videoPlayerController, - useRootNavigator: !kIsWeb, - autoPlay: true, - autoInitialize: true, - looping: true, - ); - }); - } on IOException catch (e) { - ScaffoldMessenger.of( - context, - ).showSnackBar(SnackBar(content: Text(e.toLocalizedString(context)))); - } catch (e, s) { - ErrorReporter(context, 'Unable to play video').onErrorCallback(e, s); - } - } - void _disposeControllers() { - _chewieController?.dispose(); - _videoPlayerController?.dispose(); - _chewieController = null; - _videoPlayerController = null; _mediaKitPlayer?.dispose(); _mediaKitPlayer = null; _mediaKitController = null; @@ -233,19 +135,8 @@ class EventVideoPlayerState extends State { final height = MediaQuery.of(context).size.height - 52; final width = videoWidth * (height / videoHeight); - final chewieController = _chewieController; final mediaKitController = _mediaKitController; - if (chewieController != null) { - return Center( - child: SizedBox( - width: width, - height: height, - child: Chewie(controller: chewieController), - ), - ); - } - if (mediaKitController != null) { return Center( child: SizedBox( diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index 0e9afd084..7dba15c96 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -25,7 +25,6 @@ #include #include #include -#include #include #include #include @@ -88,9 +87,6 @@ void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); - g_autoptr(FlPluginRegistrar) volume_controller_registrar = - fl_plugin_registry_get_registrar_for_plugin(registry, "VolumeControllerPlugin"); - volume_controller_plugin_register_with_registrar(volume_controller_registrar); g_autoptr(FlPluginRegistrar) webcrypto_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "WebcryptoPlugin"); webcrypto_plugin_register_with_registrar(webcrypto_registrar); diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index 4343c64ec..0a39883c5 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -22,7 +22,6 @@ list(APPEND FLUTTER_PLUGIN_LIST screen_retriever_linux sqlcipher_flutter_libs url_launcher_linux - volume_controller webcrypto window_manager window_to_front diff --git a/pubspec.lock b/pubspec.lock index 5cedc482e..7a902e127 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -274,14 +274,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.4" - chewie: - dependency: "direct main" - description: - name: chewie - sha256: "44bcfc5f0dfd1de290c87c9d86a61308b3282a70b63435d5557cfd60f54a69ca" - url: "https://pub.dev" - source: hosted - version: "1.13.0" cli_config: dependency: transitive description: @@ -1447,7 +1439,7 @@ packages: source: hosted version: "1.2.6" media_kit_libs_android_video: - dependency: "direct main" + dependency: transitive description: name: media_kit_libs_android_video sha256: "3f6274e5ab2de512c286a25c327288601ee445ed8ac319e0ef0b66148bd8f76c" @@ -1455,7 +1447,7 @@ packages: source: hosted version: "1.3.8" media_kit_libs_ios_video: - dependency: "direct main" + dependency: transitive description: name: media_kit_libs_ios_video sha256: b5382994eb37a4564c368386c154ad70ba0cc78dacdd3fb0cd9f30db6d837991 @@ -1463,7 +1455,7 @@ packages: source: hosted version: "1.1.4" media_kit_libs_linux: - dependency: "direct main" + dependency: transitive description: name: media_kit_libs_linux sha256: "2b473399a49ec94452c4d4ae51cfc0f6585074398d74216092bf3d54aac37ecf" @@ -1471,15 +1463,23 @@ packages: source: hosted version: "1.2.1" media_kit_libs_macos_video: - dependency: "direct main" + dependency: transitive description: name: media_kit_libs_macos_video sha256: f26aa1452b665df288e360393758f84b911f70ffb3878032e1aabba23aa1032d url: "https://pub.dev" source: hosted version: "1.1.4" - media_kit_libs_windows_video: + media_kit_libs_video: dependency: "direct main" + description: + name: media_kit_libs_video + sha256: "2b235b5dac79c6020e01eef5022c6cc85fedc0df1738aadc6ea489daa12a92a9" + url: "https://pub.dev" + source: hosted + version: "1.0.7" + media_kit_libs_windows_video: + dependency: transitive description: name: media_kit_libs_windows_video sha256: dff76da2778729ab650229e6b4ec6ec111eb5151431002cbd7ea304ff1f112ab @@ -1490,10 +1490,10 @@ packages: dependency: "direct main" description: name: media_kit_video - sha256: "813858c3fe84eb46679eb698695f60665e2bfbef757766fac4d2e683f926e15a" + sha256: afaa509e7b7e0bf247557a3a740cde903a52c34ace9810f94500e127bd7b043d url: "https://pub.dev" source: hosted - version: "1.3.1" + version: "2.0.1" meta: dependency: transitive description: @@ -2086,22 +2086,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.2" - screen_brightness_android: - dependency: transitive - description: - name: screen_brightness_android - sha256: "2008ad8e9527cc968f7a4de1ec58b476d495b3c612a149dbd6550c4f046da147" - url: "https://pub.dev" - source: hosted - version: "2.1.6" - screen_brightness_platform_interface: - dependency: transitive - description: - name: screen_brightness_platform_interface - sha256: "2de60c0ba569b898950029cc1f7e9dd72bda44a22beb5054aac331cb6fce2ff2" - url: "https://pub.dev" - source: hosted - version: "2.1.2" screen_retriever: dependency: transitive description: @@ -2707,46 +2691,6 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.4" - video_player: - dependency: "direct main" - description: - name: video_player - sha256: "48a7bdaa38a3d50ec10c78627abdbfad863fdf6f0d6e08c7c3c040cfd80ae36f" - url: "https://pub.dev" - source: hosted - version: "2.11.1" - video_player_android: - dependency: transitive - description: - name: video_player_android - sha256: "877a6c7ba772456077d7bfd71314629b3fe2b73733ce503fc77c3314d43a0ca0" - url: "https://pub.dev" - source: hosted - version: "2.9.5" - video_player_avfoundation: - dependency: transitive - description: - name: video_player_avfoundation - sha256: af0e5b8a7a4876fb37e7cc8cb2a011e82bb3ecfa45844ef672e32cb14a1f259e - url: "https://pub.dev" - source: hosted - version: "2.9.4" - video_player_platform_interface: - dependency: transitive - description: - name: video_player_platform_interface - sha256: "57c5d73173f76d801129d0531c2774052c5a7c11ccb962f1830630decd9f24ec" - url: "https://pub.dev" - source: hosted - version: "6.6.0" - video_player_web: - dependency: transitive - description: - name: video_player_web - sha256: "9f3c00be2ef9b76a95d94ac5119fb843dca6f2c69e6c9968f6f2b6c9e7afbdeb" - url: "https://pub.dev" - source: hosted - version: "2.4.0" vm_service: dependency: transitive description: @@ -2763,14 +2707,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.5.0" - volume_controller: - dependency: transitive - description: - name: volume_controller - sha256: "851b43150972f96fa04a16af1080be9cabcd740c1b7e91dc3f5be9461ff45010" - url: "https://pub.dev" - source: hosted - version: "3.6.0" wakelock_plus: dependency: "direct main" description: @@ -2948,5 +2884,5 @@ packages: source: hosted version: "3.1.3" sdks: - dart: ">=3.12.0 <4.0.0" - flutter: ">=3.44.0" + dart: ">=3.11.0 <4.0.0" + flutter: ">=3.41.0" diff --git a/pubspec.yaml b/pubspec.yaml index faf4ca120..703043a49 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -20,7 +20,6 @@ dependencies: badges: ^3.1.2 blurhash_dart: ^1.2.1 camera: ^0.12.0+1 - chewie: ^1.13.0 collection: ^1.18.0 cross_file: ^0.3.5+2 cupertino_icons: any @@ -102,14 +101,9 @@ dependencies: universal_html: ^2.3.0 url_launcher: ^6.3.2 video_compress: ^3.1.4 - video_player: ^2.11.1 - media_kit: ^1.1.11 - media_kit_video: ^1.2.5 - media_kit_libs_android_video: ^1.3.6 - media_kit_libs_ios_video: ^1.1.4 - media_kit_libs_macos_video: ^1.1.4 - media_kit_libs_windows_video: ^1.0.9 - media_kit_libs_linux: ^1.1.3 + media_kit: ^1.2.6 + media_kit_video: ^2.0.1 + media_kit_libs_video: ^1.0.7 vodozemac: ^0.5.0 wakelock_plus: ^1.4.0 webrtc_interface: ^1.4.0 diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 3c71c0712..2d2d53702 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -27,7 +27,6 @@ #include #include #include -#include #include #include #include @@ -75,8 +74,6 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { registry->GetRegistrarForPlugin("Sqlite3FlutterLibsPlugin")); UrlLauncherWindowsRegisterWithRegistrar( registry->GetRegistrarForPlugin("UrlLauncherWindows")); - VolumeControllerPluginCApiRegisterWithRegistrar( - registry->GetRegistrarForPlugin("VolumeControllerPluginCApi")); WebcryptoPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("WebcryptoPlugin")); WindowManagerPluginRegisterWithRegistrar( diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 57d1dbd7f..76a778fe3 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -24,7 +24,6 @@ list(APPEND FLUTTER_PLUGIN_LIST share_plus sqlcipher_flutter_libs url_launcher_windows - volume_controller webcrypto window_manager window_to_front