diff --git a/mobile/lib/pages/common/gallery_viewer.page.dart b/mobile/lib/pages/common/gallery_viewer.page.dart index 7d1ea71622..54788edc0f 100644 --- a/mobile/lib/pages/common/gallery_viewer.page.dart +++ b/mobile/lib/pages/common/gallery_viewer.page.dart @@ -119,6 +119,13 @@ class GalleryViewerPage extends HookConsumerWidget { const [], ); + useEffect(() { + final asset = loadAsset(currentIndex.value); + ref.read(castProvider.notifier).loadMedia(asset, false); + }, [ + ref.watch(castProvider).isCasting, + ]); + void showInfo() { final asset = ref.read(currentAssetProvider); if (asset == null) { diff --git a/mobile/lib/repositories/gcast.repository.dart b/mobile/lib/repositories/gcast.repository.dart index 3f38cb3e32..4d6bb296d9 100644 --- a/mobile/lib/repositories/gcast.repository.dart +++ b/mobile/lib/repositories/gcast.repository.dart @@ -14,6 +14,8 @@ class GCastRepository { void Function(CastSessionState)? onCastStatus; void Function(Map)? onCastMessage; + Map? _receiverStatus; + GCastRepository(); Future connect(CastDevice device) async { @@ -26,6 +28,9 @@ class GCastRepository { _castSession?.messageStream.listen((message) { onCastMessage?.call(message); + if (message['type'] == 'RECEIVER_STATUS') { + _receiverStatus = message; + } }); // open the default receiver @@ -38,6 +43,14 @@ class GCastRepository { } Future disconnect() async { + final sessionID = + _receiverStatus?['status']['applications'][0]['sessionId']; + + sendMessage(CastSession.kNamespaceReceiver, { + 'type': "STOP", + "sessionId": sessionID, + }); + await _castSession?.close(); } diff --git a/mobile/lib/services/gcast.service.dart b/mobile/lib/services/gcast.service.dart index 4cb7441219..341b61c9ef 100644 --- a/mobile/lib/services/gcast.service.dart +++ b/mobile/lib/services/gcast.service.dart @@ -1,5 +1,6 @@ import 'package:cast/device.dart'; import 'package:cast/session.dart'; +import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; @@ -55,12 +56,6 @@ class GCastService implements ICastDestinationService { void _onCastMessageCallback(Map message) { final msgType = message['type']; - - print(message); - - if (msgType == 'RECEIVER_STATUS') { - print("Got receiver status"); - } } Future connect(CastDevice device) async { @@ -99,8 +94,6 @@ class GCastService implements ICastDestinationService { @override void loadMedia(Asset asset, bool reload) async { - print("Casting media: ${asset.remoteId}"); - if (!isConnected) { return; } else if (asset.remoteId == null) { @@ -119,7 +112,7 @@ class GCastService implements ICastDestinationService { ); final unauthenticatedUrl = asset.isVideo - ? getOriginalUrlForRemoteId( + ? getPlaybackUrlForRemoteId( asset.remoteId!, ) : getThumbnailUrlForRemoteId( @@ -142,14 +135,12 @@ class GCastService implements ICastDestinationService { "type": "LOAD", "media": { "contentId": authenticatedURL, - "streamType": "LIVE", + "streamType": "BUFFERED", "contentType": mimeType, "contentUrl": authenticatedURL, }, "autoplay": true, }); - - print("Sending message: $authenticatedURL"); } @override diff --git a/mobile/lib/utils/image_url_builder.dart b/mobile/lib/utils/image_url_builder.dart index d063b3aa91..50218eaffd 100644 --- a/mobile/lib/utils/image_url_builder.dart +++ b/mobile/lib/utils/image_url_builder.dart @@ -73,6 +73,10 @@ String getThumbnailUrlForRemoteId( return '${Store.get(StoreKey.serverEndpoint)}/assets/$id/thumbnail?size=${type.value}'; } +String getPlaybackUrlForRemoteId(final String id) { + return '${Store.get(StoreKey.serverEndpoint)}/assets/$id/video/playback?'; +} + String getFaceThumbnailUrl(final String personId) { return '${Store.get(StoreKey.serverEndpoint)}/people/$personId/thumbnail'; } diff --git a/mobile/lib/widgets/asset_viewer/cast_dialog.dart b/mobile/lib/widgets/asset_viewer/cast_dialog.dart index 451995a09e..d39aa0e5db 100644 --- a/mobile/lib/widgets/asset_viewer/cast_dialog.dart +++ b/mobile/lib/widgets/asset_viewer/cast_dialog.dart @@ -12,6 +12,14 @@ class CastDialog extends ConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final castManager = ref.watch(castProvider); + bool isCurrentDevice(String deviceName) { + return castManager.receiverName == deviceName && castManager.isCasting; + } + + bool isDeviceConnecting(String deviceName) { + return castManager.receiverName == deviceName && !castManager.isCasting; + } + return AlertDialog( title: const Text( "cast", @@ -53,7 +61,7 @@ class CastDialog extends ConsumerWidget { title: Text( deviceName, style: TextStyle( - color: castManager.receiverName == deviceName + color: isCurrentDevice(deviceName) ? context.colorScheme.primary : null, ), @@ -62,12 +70,22 @@ class CastDialog extends ConsumerWidget { type == CastDestinationType.googleCast ? Icons.cast : Icons.cast_connected, + color: isCurrentDevice(deviceName) + ? context.colorScheme.primary + : null, ), - trailing: castManager.isCasting && - castManager.receiverName == deviceName + trailing: isCurrentDevice(deviceName) ? Icon(Icons.check, color: context.colorScheme.primary) - : null, + : isDeviceConnecting(deviceName) + ? const CircularProgressIndicator() + : null, onTap: () { + // dont accept taps if the device is already connected or is connecting now + if (isDeviceConnecting(deviceName) || + castManager.isCasting) { + return; + } + ref.read(castProvider.notifier).connect(type, deviceObj); }, );