socket.dart 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. import 'dart:async';
  2. import 'dart:convert';
  3. import 'dart:io';
  4. import 'package:get/get.dart';
  5. import 'package:nanoid/nanoid.dart';
  6. import 'package:web_socket_channel/io.dart';
  7. import './actions.dart' as actions;
  8. import './controller.dart';
  9. String getWebSocketUrl(String apiUrl) {
  10. return "wss://$apiUrl/pubsub";
  11. }
  12. String getUniqueName(String name) {
  13. return "$name-${nanoid(5)}";
  14. }
  15. const socketKeepaliveTimeoutMs = 30000;
  16. class Socket {
  17. WebSocket conn;
  18. IOWebSocketChannel channel;
  19. RxBool connected = false.obs;
  20. RxString error = ''.obs;
  21. void _keepalive() {
  22. var future = new Future.delayed(const Duration(milliseconds: socketKeepaliveTimeoutMs));
  23. void ping(dynamic data) {
  24. channel.sink.add(jsonEncode({'type': 'PING'}));
  25. _keepalive();
  26. }
  27. var subscription = future.asStream().listen(ping);
  28. channel.sink.done.whenComplete(() {
  29. subscription.cancel();
  30. });
  31. }
  32. Future<String> connect(
  33. String apiUrl,
  34. String name,
  35. void Function(String) onRemoteMessage,
  36. ) async {
  37. if (apiUrl == null || name == null || apiUrl.length == 0 || name.length == 0) {
  38. return null;
  39. }
  40. final String uniqueName = getUniqueName(name);
  41. final String webSocketUrl = getWebSocketUrl(apiUrl);
  42. final String pubsubUrl = "$webSocketUrl?client-name=$uniqueName";
  43. connected.value = false;
  44. try {
  45. conn = await WebSocket
  46. .connect(Uri.parse(pubsubUrl).toString())
  47. .timeout(Duration(seconds: 10));
  48. channel = IOWebSocketChannel(conn);
  49. channel.stream.listen((message) => onRemoteMessage(message));
  50. connected.value = true;
  51. error.value = '';
  52. _keepalive();
  53. }
  54. on SocketException {
  55. error.value = "Error connecting to socket";
  56. }
  57. on TimeoutException {
  58. error.value = "Timeout connecting to socket";
  59. }
  60. catch (err) {
  61. error.value = "Unknown error connecting to socket";
  62. }
  63. return uniqueName;
  64. }
  65. void disconnect() {
  66. conn?.close();
  67. }
  68. void dispatch(String action) {
  69. if (channel == null) {
  70. return;
  71. }
  72. channel.sink.add(action);
  73. }
  74. }
  75. void onRemoteMessage(Controller controller, String message) {
  76. var action = jsonDecode(message);
  77. switch (action['type']) {
  78. case actions.CLIENT_LIST_UPDATED:
  79. var payload = action['payload'];
  80. List<Client> clientList = [];
  81. for (var i = 0; i < payload.length; i++) {
  82. clientList.add(new Client(
  83. payload[i]['name'],
  84. payload[i]['lastPing'],
  85. ));
  86. }
  87. controller.setClients(clientList);
  88. break;
  89. case actions.STATE_SET:
  90. Player nextPlayer = new Player();
  91. nextPlayer.currentTime = action['payload']['currentTime'].toDouble();
  92. nextPlayer.seekTime = action['payload']['seekTime'].toDouble();
  93. nextPlayer.master = action['payload']['master'];
  94. nextPlayer.songId = action['payload']['songId'];
  95. nextPlayer.playing = action['payload']['playing'];
  96. nextPlayer.shuffleMode = action['payload']['shuffleMode'];
  97. nextPlayer.queue = [];
  98. for (var i = 0; i < action['payload']['queue'].length; i++) {
  99. nextPlayer.queue.add(action['payload']['queue'][i]);
  100. }
  101. nextPlayer.activeClients = [];
  102. for (var i = 0; i < action['payload']['activeClients'].length; i++) {
  103. nextPlayer.activeClients.add(action['payload']['activeClients'][i]);
  104. }
  105. controller.player.value = nextPlayer;
  106. break;
  107. }
  108. }