placeholder widget for video support

This commit is contained in:
Henry Hiles 2026-05-19 16:05:45 -04:00
commit 1a4ef800c6
Signed by: Henry-Hiles
SSH key fingerprint: SHA256:VKQUdS31Q90KvX7EkKMHMBpUspcmItAh86a+v7PGiIs
12 changed files with 265 additions and 54 deletions

12
flake.lock generated
View file

@ -5,11 +5,11 @@
"nixpkgs-lib": "nixpkgs-lib" "nixpkgs-lib": "nixpkgs-lib"
}, },
"locked": { "locked": {
"lastModified": 1777988971, "lastModified": 1778716662,
"narHash": "sha256-qIoWPDs+0/8JecyYgE3gpKQxW/4bLW/gp45vow9ioCQ=", "narHash": "sha256-m1Yf0wZ8j1OHjTc2UwHwyQRSnNeSgLJOd7q5Y45hzi4=",
"owner": "hercules-ci", "owner": "hercules-ci",
"repo": "flake-parts", "repo": "flake-parts",
"rev": "0678d8986be1661af6bb555f3489f2fdfc31f6ff", "rev": "f7c1a2d347e4c52d5fb8d10cb4d94b5884e546fb",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -88,11 +88,11 @@
}, },
"nixpkgs_2": { "nixpkgs_2": {
"locked": { "locked": {
"lastModified": 1777954456, "lastModified": 1778869304,
"narHash": "sha256-hGdgeU2Nk87RAuZyYjyDjFL6LK7dAZN5RE9+hrDTkDU=", "narHash": "sha256-30sZNZoA1cqF5JNO9fVX+wgiQYjB7HJqqJ4ztCDeBZE=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "549bd84d6279f9852cae6225e372cc67fb91a4c1", "rev": "d233902339c02a9c334e7e593de68855ad26c4cb",
"type": "github" "type": "github"
}, },
"original": { "original": {

View file

@ -2,6 +2,7 @@ import "dart:io";
import "package:fast_immutable_collections/fast_immutable_collections.dart"; import "package:fast_immutable_collections/fast_immutable_collections.dart";
import "package:flutter/foundation.dart"; import "package:flutter/foundation.dart";
import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:media_kit/media_kit.dart";
import "package:nexus/controllers/client_controller.dart"; import "package:nexus/controllers/client_controller.dart";
import "package:nexus/controllers/client_state_controller.dart"; import "package:nexus/controllers/client_state_controller.dart";
import "package:nexus/controllers/header_controller.dart"; import "package:nexus/controllers/header_controller.dart";
@ -56,6 +57,7 @@ void showError(Object error, [StackTrace? stackTrace]) {
void main() async { void main() async {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
MediaKit.ensureInitialized();
if (Platform.isLinux || Platform.isMacOS || Platform.isWindows) { if (Platform.isLinux || Platform.isMacOS || Platform.isWindows) {
await windowManager.ensureInitialized(); await windowManager.ensureInitialized();

View file

@ -3,6 +3,7 @@ import "package:nexus/models/info/audio.dart";
import "package:nexus/models/content/content.dart"; import "package:nexus/models/content/content.dart";
import "package:nexus/models/info/file.dart"; import "package:nexus/models/info/file.dart";
import "package:nexus/models/info/image.dart"; import "package:nexus/models/info/image.dart";
import "package:nexus/models/info/video.dart";
part "message.freezed.dart"; part "message.freezed.dart";
part "message.g.dart"; part "message.g.dart";
@ -72,7 +73,7 @@ abstract class MessageContent extends Content with _$MessageContent {
String? formattedBody, String? formattedBody,
// EncryptedFile? file // EncryptedFile? file
String? filename, String? filename,
AudioInfo? info, VideoInfo? info,
Uri? url, Uri? url,
}) = VideoMessageContent; }) = VideoMessageContent;

View file

@ -26,6 +26,7 @@ import "package:nexus/widgets/chat_page/lazy_loading/message_avatar.dart";
import "package:nexus/widgets/chat_page/lazy_loading/message_displayname.dart"; import "package:nexus/widgets/chat_page/lazy_loading/message_displayname.dart";
import "package:nexus/widgets/link_preview.dart"; import "package:nexus/widgets/link_preview.dart";
import "package:nexus/widgets/loading.dart"; import "package:nexus/widgets/loading.dart";
import "package:nexus/widgets/players/video.dart";
import "package:timeago/timeago.dart"; import "package:timeago/timeago.dart";
import "package:flutter_linkify/flutter_linkify.dart"; import "package:flutter_linkify/flutter_linkify.dart";
@ -153,6 +154,21 @@ class RenderEvent extends ConsumerWidget {
:final body, :final body,
:final formattedBody, :final formattedBody,
:final format, :final format,
) ||
VideoMessageContent(
:final body,
:final formattedBody,
:final format,
) ||
AudioMessageContent(
:final body,
:final formattedBody,
:final format,
) ||
FileMessageContent(
:final body,
:final formattedBody,
:final format,
) => Column( ) => Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@ -199,9 +215,11 @@ class RenderEvent extends ConsumerWidget {
if (!textOnly) if (!textOnly)
if (event.content if (event.content
case ImageMessageContent( case ImageMessageContent(
:final url, :final url,
:final info, ) ||
)) FileMessageContent(:final url) ||
VideoMessageContent(:final url) ||
AudioMessageContent(:final url))
switch (url?.mxcToHttps( switch (url?.mxcToHttps(
ref.watch( ref.watch(
ClientStateController.provider ClientStateController.provider
@ -215,32 +233,37 @@ class RenderEvent extends ConsumerWidget {
constraints: BoxConstraints.loose( constraints: BoxConstraints.loose(
Size.fromWidth(500), Size.fromWidth(500),
), ),
child: ExpandableImage( child: switch (event.content) {
url.toString(), VideoMessageContent(
child: ClipRRect( :final info,
borderRadius: BorderRadius.all( ) =>
Radius.circular(8), VideoPlayer(url, info),
), ImageMessageContent(:final info) => ExpandableImage(
child: Image( url.toString(),
image: CachedNetworkImage( child: ClipRRect(
url.toString(), borderRadius:
ref.watch( BorderRadius.all(
CrossCacheController Radius.circular(8),
.provider, ),
child: Image(
image: CachedNetworkImage(
url.toString(),
ref.watch(
CrossCacheController
.provider,
),
headers: ref.headers,
), ),
headers: ref.headers, width: info?.width,
), loadingBuilder:
width: info?.width, (
loadingBuilder: _,
( child,
_, loadingProgress,
child, ) => loadingProgress == null
loadingProgress, ? child
) => loadingProgress == null : switch (info?.blurHash) {
? child final blurHash? => SizedBox(
: switch (info?.blurHash) {
final blurHash? =>
SizedBox(
width: width:
info?.width ?? info?.width ??
info?.height ?? info?.height ??
@ -253,26 +276,28 @@ class RenderEvent extends ConsumerWidget {
hash: blurHash, hash: blurHash,
), ),
), ),
_ => Loading(), _ => Loading(),
}, },
errorBuilder: errorBuilder:
( (
context, context,
error, error,
stackTrace, stackTrace,
) => Center( ) => Center(
child: Text( child: Text(
"Image Failed to Load", "Image Failed to Load",
style: TextStyle( style: TextStyle(
color: Theme.of( color: Theme.of(
context, context,
).colorScheme.error, ).colorScheme.error,
),
), ),
), ),
), ),
), ),
), ),
), _ => SizedBox.shrink(),
},
), ),
_ => Text( _ => Text(
"Nexus currently cannot handle encrypted media", "Nexus currently cannot handle encrypted media",

View file

@ -0,0 +1,27 @@
import "package:flutter/material.dart";
import "package:hooks_riverpod/hooks_riverpod.dart";
import "package:nexus/models/info/video.dart";
// import "package:flutter_hooks/flutter_hooks.dart";
// import "package:media_kit/media_kit.dart";
// import "package:media_kit_video/media_kit_video.dart";
// import "package:nexus/helpers/extensions/get_headers.dart";
class VideoPlayer extends HookConsumerWidget {
final VideoInfo? info;
final Uri url;
const VideoPlayer(this.url, this.info, {super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
// final player = useMemoized(Player.new);
// final controller = useMemoized(() => VideoController(player), [player]);
// useEffect(() {
// player.open(Media(url.toString(), httpHeaders: ref.headers), play: false);
// return player.dispose;
// }, []);
return Placeholder();
}
}

View file

@ -8,6 +8,8 @@
#include <dynamic_system_colors/dynamic_color_plugin.h> #include <dynamic_system_colors/dynamic_color_plugin.h>
#include <file_selector_linux/file_selector_plugin.h> #include <file_selector_linux/file_selector_plugin.h>
#include <media_kit_libs_linux/media_kit_libs_linux_plugin.h>
#include <media_kit_video/media_kit_video_plugin.h>
#include <screen_retriever_linux/screen_retriever_linux_plugin.h> #include <screen_retriever_linux/screen_retriever_linux_plugin.h>
#include <url_launcher_linux/url_launcher_plugin.h> #include <url_launcher_linux/url_launcher_plugin.h>
#include <window_manager/window_manager_plugin.h> #include <window_manager/window_manager_plugin.h>
@ -19,6 +21,12 @@ void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) file_selector_linux_registrar = g_autoptr(FlPluginRegistrar) file_selector_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin"); fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin");
file_selector_plugin_register_with_registrar(file_selector_linux_registrar); file_selector_plugin_register_with_registrar(file_selector_linux_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) screen_retriever_linux_registrar = g_autoptr(FlPluginRegistrar) screen_retriever_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverLinuxPlugin"); fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverLinuxPlugin");
screen_retriever_linux_plugin_register_with_registrar(screen_retriever_linux_registrar); screen_retriever_linux_plugin_register_with_registrar(screen_retriever_linux_registrar);

View file

@ -5,6 +5,8 @@
list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_PLUGIN_LIST
dynamic_system_colors dynamic_system_colors
file_selector_linux file_selector_linux
media_kit_libs_linux
media_kit_video
screen_retriever_linux screen_retriever_linux
url_launcher_linux url_launcher_linux
window_manager window_manager

View file

@ -22,7 +22,14 @@ pkgs.mkShell {
go go
git git
jdk17 jdk17
flutter libGL
wayland
(flutter.override {
extraPkgConfigPackages = [
mpv-unwrapped
libass
];
})
android.platform-tools android.platform-tools
]; ];

View file

@ -760,6 +760,70 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.13.0" version: "0.13.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: transitive
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: transitive
description:
name: media_kit_libs_ios_video
sha256: b5382994eb37a4564c368386c154ad70ba0cc78dacdd3fb0cd9f30db6d837991
url: "https://pub.dev"
source: hosted
version: "1.1.4"
media_kit_libs_linux:
dependency: transitive
description:
name: media_kit_libs_linux
sha256: "2b473399a49ec94452c4d4ae51cfc0f6585074398d74216092bf3d54aac37ecf"
url: "https://pub.dev"
source: hosted
version: "1.2.1"
media_kit_libs_macos_video:
dependency: transitive
description:
name: media_kit_libs_macos_video
sha256: f26aa1452b665df288e360393758f84b911f70ffb3878032e1aabba23aa1032d
url: "https://pub.dev"
source: hosted
version: "1.1.4"
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
url: "https://pub.dev"
source: hosted
version: "1.0.11"
media_kit_video:
dependency: "direct main"
description:
name: media_kit_video
sha256: afaa509e7b7e0bf247557a3a740cde903a52c34ace9810f94500e127bd7b043d
url: "https://pub.dev"
source: hosted
version: "2.0.1"
meta: meta:
dependency: transitive dependency: transitive
description: description:
@ -808,6 +872,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.2.0" version: "2.2.0"
package_info_plus:
dependency: transitive
description:
name: package_info_plus
sha256: "468c26b4254ab01979fa5e4a98cb343ea3631b9acee6f21028997419a80e1a20"
url: "https://pub.dev"
source: hosted
version: "9.0.1"
package_info_plus_platform_interface:
dependency: transitive
description:
name: package_info_plus_platform_interface
sha256: "202a487f08836a592a6bd4f901ac69b3a8f146af552bbd14407b6b41e1c3f086"
url: "https://pub.dev"
source: hosted
version: "3.2.1"
path: path:
dependency: "direct main" dependency: "direct main"
description: description:
@ -976,6 +1056,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.28.0" version: "0.28.0"
safe_local_storage:
dependency: transitive
description:
name: safe_local_storage
sha256: "287ea1f667c0b93cdc127dccc707158e2d81ee59fba0459c31a0c7da4d09c755"
url: "https://pub.dev"
source: hosted
version: "2.0.3"
screen_retriever: screen_retriever:
dependency: transitive dependency: transitive
description: description:
@ -1277,6 +1365,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.3.1" version: "2.3.1"
universal_platform:
dependency: transitive
description:
name: universal_platform
sha256: "64e16458a0ea9b99260ceb5467a214c1f298d647c659af1bff6d3bf82536b1ec"
url: "https://pub.dev"
source: hosted
version: "1.1.0"
uri_parser:
dependency: transitive
description:
name: uri_parser
sha256: "051c62e5f693de98ca9f130ee707f8916e2266945565926be3ff20659f7853ce"
url: "https://pub.dev"
source: hosted
version: "3.0.2"
url_launcher: url_launcher:
dependency: "direct main" dependency: "direct main"
description: description:
@ -1341,6 +1445,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.1.5" version: "3.1.5"
uuid:
dependency: transitive
description:
name: uuid
sha256: "1fef9e8e11e2991bb773070d4656b7bd5d850967a2456cfc83cf47925ba79489"
url: "https://pub.dev"
source: hosted
version: "4.5.3"
vector_graphics: vector_graphics:
dependency: transitive dependency: transitive
description: description:
@ -1381,6 +1493,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "15.2.0" version: "15.2.0"
wakelock_plus:
dependency: transitive
description:
name: wakelock_plus
sha256: ddf3db70eaa10c37558ff817519b85d527dbd21034fd5d8e1c2e85f31588f1c1
url: "https://pub.dev"
source: hosted
version: "1.5.2"
wakelock_plus_platform_interface:
dependency: transitive
description:
name: wakelock_plus_platform_interface
sha256: b13f99e992e7ae6a152e16c5559d3c07ff445b13330192662494e614ca3e7d7b
url: "https://pub.dev"
source: hosted
version: "1.5.1"
watcher: watcher:
dependency: transitive dependency: transitive
description: description:
@ -1470,5 +1598,5 @@ packages:
source: hosted source: hosted
version: "2.2.4" version: "2.2.4"
sdks: sdks:
dart: "3.11.4" dart: "3.11.5"
flutter: ">=3.38.4" flutter: ">=3.38.4"

View file

@ -9,7 +9,7 @@ flutter:
uses-material-design: true uses-material-design: true
environment: environment:
sdk: "3.11.4" sdk: "3.11.5"
dependency_overrides: dependency_overrides:
linkify: linkify:
@ -60,6 +60,9 @@ dependencies:
url: https://github.com/Henry-Hiles/emoji_text_field url: https://github.com/Henry-Hiles/emoji_text_field
flutter_blurhash: 0.9.1 flutter_blurhash: 0.9.1
super_sliver_list: 0.4.1 super_sliver_list: 0.4.1
media_kit: 1.2.6
media_kit_video: 2.0.1
media_kit_libs_video: 1.0.7
dev_dependencies: dev_dependencies:
build_runner: 2.15.0 build_runner: 2.15.0

View file

@ -8,6 +8,8 @@
#include <dynamic_system_colors/dynamic_color_plugin_c_api.h> #include <dynamic_system_colors/dynamic_color_plugin_c_api.h>
#include <file_selector_windows/file_selector_windows.h> #include <file_selector_windows/file_selector_windows.h>
#include <media_kit_libs_windows_video/media_kit_libs_windows_video_plugin_c_api.h>
#include <media_kit_video/media_kit_video_plugin_c_api.h>
#include <screen_retriever_windows/screen_retriever_windows_plugin_c_api.h> #include <screen_retriever_windows/screen_retriever_windows_plugin_c_api.h>
#include <url_launcher_windows/url_launcher_windows.h> #include <url_launcher_windows/url_launcher_windows.h>
#include <window_manager/window_manager_plugin.h> #include <window_manager/window_manager_plugin.h>
@ -17,6 +19,10 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
registry->GetRegistrarForPlugin("DynamicColorPluginCApi")); registry->GetRegistrarForPlugin("DynamicColorPluginCApi"));
FileSelectorWindowsRegisterWithRegistrar( FileSelectorWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FileSelectorWindows")); registry->GetRegistrarForPlugin("FileSelectorWindows"));
MediaKitLibsWindowsVideoPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("MediaKitLibsWindowsVideoPluginCApi"));
MediaKitVideoPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("MediaKitVideoPluginCApi"));
ScreenRetrieverWindowsPluginCApiRegisterWithRegistrar( ScreenRetrieverWindowsPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("ScreenRetrieverWindowsPluginCApi")); registry->GetRegistrarForPlugin("ScreenRetrieverWindowsPluginCApi"));
UrlLauncherWindowsRegisterWithRegistrar( UrlLauncherWindowsRegisterWithRegistrar(

View file

@ -5,6 +5,8 @@
list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_PLUGIN_LIST
dynamic_system_colors dynamic_system_colors
file_selector_windows file_selector_windows
media_kit_libs_windows_video
media_kit_video
screen_retriever_windows screen_retriever_windows
url_launcher_windows url_launcher_windows
window_manager window_manager