mirror of
https://github.com/immich-app/immich.git
synced 2025-06-01 04:36:19 -04:00
casting works!
just need to add session key check and remote video controls
This commit is contained in:
parent
4d86773ffe
commit
f5ed1360e6
@ -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) {
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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';
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user