import 'package:just_audio/just_audio.dart' show LoopMode; import '../playback/playback_engine.dart'; import '../subsonic/models.dart'; import 'messages.dart'; import 'remote_session.dart'; /// Map a wire [StateSnapshot] back into the app's [PlaybackState] so the UI can /// render a remote device's playback exactly as it renders local playback. PlaybackState playbackStateFromSnapshot(StateSnapshot s) => PlaybackState( queue: s.queue, currentIndex: s.currentIndex, playing: s.playing, position: Duration(milliseconds: s.positionMs), duration: Duration(milliseconds: s.durationMs), shuffle: s.shuffle, loop: LoopMode.values.firstWhere( (m) => m.name == s.loop, orElse: () => LoopMode.off, ), supported: true, ); /// The command half of the facade while attached to another device: every /// [PlaybackCommands] call is serialized into a [RemoteCommand] and sent to the /// host, which applies it to *its* engine. Playback state comes back the other /// way as snapshots (see `remote_providers.dart`), so this proxy holds none. /// /// Commands are fire-and-forget: LAN round-trips are sub-frame, and the host's /// next snapshot is the source of truth, so there is no optimistic local edit /// to reconcile. class RemotePlaybackProxy implements PlaybackCommands { RemotePlaybackProxy(this._session); final RemoteSession _session; @override Future playSongs(List songs, {int startIndex = 0}) async => _session.send(RemoteCommand.playSongs(songs, startIndex: startIndex)); @override Future jumpTo(int index) async => _session.send(RemoteCommand.jumpTo(index)); @override Future playNext(Song song) async => _session.send(RemoteCommand.playNext(song)); @override Future addToQueue(Song song) async => _session.send(RemoteCommand.addToQueue(song)); @override Future removeAt(int index) async => _session.send(RemoteCommand.removeAt(index)); @override Future reorderQueue(int oldIndex, int newIndex) async => _session.send(RemoteCommand.reorder(oldIndex, newIndex)); @override Future togglePlayPause() async => _session.send(const RemoteCommand.playPause()); @override Future next() async => _session.send(const RemoteCommand.next()); @override Future previous() async => _session.send(const RemoteCommand.previous()); @override Future seek(Duration position) async => _session.send(RemoteCommand.seek(position.inMilliseconds)); @override Future toggleShuffle() async => _session.send(const RemoteCommand.toggleShuffle()); @override Future cycleLoop() async => _session.send(const RemoteCommand.cycleLoop()); @override Future retry() async => _session.send(const RemoteCommand.retry()); @override void resyncFromPlayer() { // No-op: the remote device owns the player; we receive its state pushed to // us and have no local playhead to re-read. } }