casting works!

just need to add session key check and remote video controls
This commit is contained in:
bwees 2025-05-19 15:47:24 -05:00
parent 4d86773ffe
commit f5ed1360e6
No known key found for this signature in database
5 changed files with 49 additions and 16 deletions

View File

@ -119,6 +119,13 @@ class GalleryViewerPage extends HookConsumerWidget {
const [], const [],
); );
useEffect(() {
final asset = loadAsset(currentIndex.value);
ref.read(castProvider.notifier).loadMedia(asset, false);
}, [
ref.watch(castProvider).isCasting,
]);
void showInfo() { void showInfo() {
final asset = ref.read(currentAssetProvider); final asset = ref.read(currentAssetProvider);
if (asset == null) { if (asset == null) {

View File

@ -14,6 +14,8 @@ class GCastRepository {
void Function(CastSessionState)? onCastStatus; void Function(CastSessionState)? onCastStatus;
void Function(Map<String, dynamic>)? onCastMessage; void Function(Map<String, dynamic>)? onCastMessage;
Map<String, dynamic>? _receiverStatus;
GCastRepository(); GCastRepository();
Future<void> connect(CastDevice device) async { Future<void> connect(CastDevice device) async {
@ -26,6 +28,9 @@ class GCastRepository {
_castSession?.messageStream.listen((message) { _castSession?.messageStream.listen((message) {
onCastMessage?.call(message); onCastMessage?.call(message);
if (message['type'] == 'RECEIVER_STATUS') {
_receiverStatus = message;
}
}); });
// open the default receiver // open the default receiver
@ -38,6 +43,14 @@ class GCastRepository {
} }
Future<void> disconnect() async { Future<void> disconnect() async {
final sessionID =
_receiverStatus?['status']['applications'][0]['sessionId'];
sendMessage(CastSession.kNamespaceReceiver, {
'type': "STOP",
"sessionId": sessionID,
});
await _castSession?.close(); await _castSession?.close();
} }

View File

@ -1,5 +1,6 @@
import 'package:cast/device.dart'; import 'package:cast/device.dart';
import 'package:cast/session.dart'; import 'package:cast/session.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/domain/models/store.model.dart';
import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/entities/asset.entity.dart';
@ -55,12 +56,6 @@ class GCastService implements ICastDestinationService {
void _onCastMessageCallback(Map<String, dynamic> message) { void _onCastMessageCallback(Map<String, dynamic> message) {
final msgType = message['type']; final msgType = message['type'];
print(message);
if (msgType == 'RECEIVER_STATUS') {
print("Got receiver status");
}
} }
Future<void> connect(CastDevice device) async { Future<void> connect(CastDevice device) async {
@ -99,8 +94,6 @@ class GCastService implements ICastDestinationService {
@override @override
void loadMedia(Asset asset, bool reload) async { void loadMedia(Asset asset, bool reload) async {
print("Casting media: ${asset.remoteId}");
if (!isConnected) { if (!isConnected) {
return; return;
} else if (asset.remoteId == null) { } else if (asset.remoteId == null) {
@ -119,7 +112,7 @@ class GCastService implements ICastDestinationService {
); );
final unauthenticatedUrl = asset.isVideo final unauthenticatedUrl = asset.isVideo
? getOriginalUrlForRemoteId( ? getPlaybackUrlForRemoteId(
asset.remoteId!, asset.remoteId!,
) )
: getThumbnailUrlForRemoteId( : getThumbnailUrlForRemoteId(
@ -142,14 +135,12 @@ class GCastService implements ICastDestinationService {
"type": "LOAD", "type": "LOAD",
"media": { "media": {
"contentId": authenticatedURL, "contentId": authenticatedURL,
"streamType": "LIVE", "streamType": "BUFFERED",
"contentType": mimeType, "contentType": mimeType,
"contentUrl": authenticatedURL, "contentUrl": authenticatedURL,
}, },
"autoplay": true, "autoplay": true,
}); });
print("Sending message: $authenticatedURL");
} }
@override @override

View File

@ -73,6 +73,10 @@ String getThumbnailUrlForRemoteId(
return '${Store.get(StoreKey.serverEndpoint)}/assets/$id/thumbnail?size=${type.value}'; 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) { String getFaceThumbnailUrl(final String personId) {
return '${Store.get(StoreKey.serverEndpoint)}/people/$personId/thumbnail'; return '${Store.get(StoreKey.serverEndpoint)}/people/$personId/thumbnail';
} }

View File

@ -12,6 +12,14 @@ class CastDialog extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final castManager = ref.watch(castProvider); 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( return AlertDialog(
title: const Text( title: const Text(
"cast", "cast",
@ -53,7 +61,7 @@ class CastDialog extends ConsumerWidget {
title: Text( title: Text(
deviceName, deviceName,
style: TextStyle( style: TextStyle(
color: castManager.receiverName == deviceName color: isCurrentDevice(deviceName)
? context.colorScheme.primary ? context.colorScheme.primary
: null, : null,
), ),
@ -62,12 +70,22 @@ class CastDialog extends ConsumerWidget {
type == CastDestinationType.googleCast type == CastDestinationType.googleCast
? Icons.cast ? Icons.cast
: Icons.cast_connected, : Icons.cast_connected,
color: isCurrentDevice(deviceName)
? context.colorScheme.primary
: null,
), ),
trailing: castManager.isCasting && trailing: isCurrentDevice(deviceName)
castManager.receiverName == deviceName
? Icon(Icons.check, color: context.colorScheme.primary) ? Icon(Icons.check, color: context.colorScheme.primary)
: isDeviceConnecting(deviceName)
? const CircularProgressIndicator()
: null, : null,
onTap: () { 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); ref.read(castProvider.notifier).connect(type, deviceObj);
}, },
); );