socket.dart 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. import 'dart:async';
  2. import 'dart:convert';
  3. import 'package:nanoid/nanoid.dart';
  4. import 'package:web_socket_channel/io.dart';
  5. import './actions.dart' as actions;
  6. import './controller.dart';
  7. String getWebSocketUrl(String apiUrl) {
  8. return "wss://$apiUrl/pubsub";
  9. }
  10. String getUniqueName(String name) {
  11. return "$name-${nanoid(5)}";
  12. }
  13. const socketKeepaliveTimeoutMs = 30000;
  14. void keepalive(IOWebSocketChannel channel) {
  15. var future = new Future.delayed(const Duration(milliseconds: socketKeepaliveTimeoutMs));
  16. void ping(dynamic data) {
  17. channel.sink.add(jsonEncode({'type': 'PING'}));
  18. keepalive(channel);
  19. }
  20. var subscription = future.asStream().listen(ping);
  21. channel.sink.done.whenComplete(() {
  22. subscription.cancel();
  23. });
  24. }
  25. void onRemoteMessage(Controller controller, String message) {
  26. var action = jsonDecode(message);
  27. switch (action['type']) {
  28. case actions.CLIENT_LIST_UPDATED:
  29. var payload = action['payload'];
  30. List<Client> clientList = [];
  31. for (var i = 0; i < payload.length; i++) {
  32. clientList.add(new Client(
  33. payload[i]['name'],
  34. payload[i]['lastPing'],
  35. ));
  36. }
  37. controller.setClients(clientList);
  38. break;
  39. case actions.STATE_SET:
  40. Player nextPlayer = new Player();
  41. nextPlayer.currentTime = action['payload']['currentTime'].toDouble();
  42. nextPlayer.seekTime = action['payload']['seekTime'].toDouble();
  43. nextPlayer.master = action['payload']['master'];
  44. nextPlayer.songId = action['payload']['songId'];
  45. nextPlayer.playing = action['payload']['playing'];
  46. nextPlayer.queue = [];
  47. for (var i = 0; i < action['payload']['queue'].length; i++) {
  48. nextPlayer.queue.add(action['payload']['queue'][i]);
  49. }
  50. controller.player.value = nextPlayer;
  51. break;
  52. }
  53. }
  54. void connect(Controller controller) {
  55. if (controller == null ||
  56. controller.name.value.length == 0 ||
  57. controller.apiUrl.value.length == 0) {
  58. return;
  59. }
  60. final String uniqueName = getUniqueName(controller.name.value);
  61. controller.uniqueName.value = uniqueName;
  62. final String webSocketUrl = getWebSocketUrl(controller.apiUrl.value);
  63. final String pubsubUrl = "$webSocketUrl?client-name=$uniqueName";
  64. var channel = IOWebSocketChannel.connect(Uri.parse(pubsubUrl));
  65. controller.channel = channel;
  66. channel.stream.listen((message) {
  67. onRemoteMessage(controller, message);
  68. });
  69. controller.connected.value = true;
  70. keepalive(channel);
  71. }
  72. void disconnect(Controller controller) {
  73. controller.connected.value = false;
  74. if (controller.channel != null) {
  75. controller.channel.sink.close();
  76. }
  77. controller.uniqueName.value = '';
  78. }
  79. void setApiUrl(Controller controller, String apiUrl) {
  80. disconnect(controller);
  81. controller.setApiUrl(apiUrl);
  82. connect(controller);
  83. }