import 'dart:convert'; import 'package:catcher_2/catcher_2.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:spotify/spotify.dart'; import 'package:spotube/models/connect/connect.dart'; import 'package:spotube/provider/connect/clients.dart'; import 'package:spotube/provider/proxy_playlist/proxy_playlist.dart'; import 'package:spotube/services/audio_player/loop_mode.dart'; import 'package:web_socket_channel/web_socket_channel.dart'; import 'package:web_socket_channel/status.dart' as status; final playingProvider = StateProvider( (ref) => false, ); final positionProvider = StateProvider( (ref) => Duration.zero, ); final durationProvider = StateProvider( (ref) => Duration.zero, ); final shuffleProvider = StateProvider( (ref) => false, ); final loopModeProvider = StateProvider( (ref) => PlaybackLoopMode.none, ); final queueProvider = StateProvider( (ref) => ProxyPlaylist({}), ); final volumeProvider = StateProvider( (ref) => 1.0, ); class ConnectNotifier extends AsyncNotifier { @override build() async { try { final connectClients = ref.watch(connectClientsProvider); print('Building ConnectNotifier'); if (connectClients.asData?.value.resolvedService == null) return null; final service = connectClients.asData!.value.resolvedService!; print( 'Connecting to ${service.name}: ws://${service.host}:${service.port}/ws'); final channel = WebSocketChannel.connect( Uri.parse('ws://${service.host}:${service.port}/ws'), ); await channel.ready; print( 'Connected to ${service.name}: ws://${service.host}:${service.port}/ws'); final subscription = channel.stream.listen( (message) { final event = WebSocketEvent.fromJson(jsonDecode(message), (data) => data); event.onQueue((event) { ref.read(queueProvider.notifier).state = event.data; }); event.onPlaying((event) { ref.read(playingProvider.notifier).state = event.data; }); event.onPosition((event) { ref.read(positionProvider.notifier).state = event.data; }); event.onDuration((event) { ref.read(durationProvider.notifier).state = event.data; }); event.onShuffle((event) { ref.read(shuffleProvider.notifier).state = event.data; }); event.onLoop((event) { ref.read(loopModeProvider.notifier).state = event.data; }); event.onVolume((event) { ref.read(volumeProvider.notifier).state = event.data; }); }, onError: (error) { Catcher2.reportCheckedError( error, StackTrace.current, ); }, ); ref.onDispose(() { subscription.cancel(); channel.sink.close(status.goingAway); }); return channel; } catch (e, stack) { Catcher2.reportCheckedError(e, stack); rethrow; } } Future emit(Object message) async { if (state.value == null) return; state.value?.sink.add( message is String ? message : (message as dynamic).toJson(), ); } Future resume() async { emit(WebSocketResumeEvent()); } Future pause() async { emit(WebSocketPauseEvent()); } Future stop() async { emit(WebSocketStopEvent()); } Future jumpTo(int position) async { emit(WebSocketJumpEvent(position)); } Future load(WebSocketLoadEventData data) async { emit(WebSocketLoadEvent(data)); } Future next() async { emit(WebSocketNextEvent()); } Future previous() async { emit(WebSocketPreviousEvent()); } Future seek(Duration position) async { emit(WebSocketSeekEvent(position)); } Future setShuffle(bool value) async { emit(WebSocketShuffleEvent(value)); } Future setLoopMode(PlaybackLoopMode value) async { emit(WebSocketLoopEvent(value)); } Future addTrack(Track data) async { emit(WebSocketAddTrackEvent(data)); } Future removeTrack(String data) async { emit(WebSocketRemoveTrackEvent(data)); } Future reorder(ReorderData data) async { emit(WebSocketReorderEvent(data)); } Future setVolume(double value) async { emit(WebSocketVolumeEvent(value)); } } final connectProvider = AsyncNotifierProvider( () => ConnectNotifier(), );