import 'package:audio_service/audio_service.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../core/models.dart'; import '../data/catalog_repository.dart'; import '../offline/download_store.dart'; import 'nk_audio_handler.dart'; /// Created in main() by AudioService.init and injected with an override. final audioHandlerProvider = Provider( (ref) => throw UnimplementedError('audioHandlerProvider must be overridden in main()'), ); final mediaItemProvider = StreamProvider((ref) => ref.watch(audioHandlerProvider).mediaItem); final playbackStateProvider = StreamProvider((ref) => ref.watch(audioHandlerProvider).playbackState); final queueProvider = StreamProvider>((ref) => ref.watch(audioHandlerProvider).queue); final positionProvider = StreamProvider((ref) => ref.watch(audioHandlerProvider).positionStream); /// The catalog track behind the current media item, if any. final currentTrackProvider = Provider((ref) { final id = ref.watch(mediaItemProvider).value?.id; final catalog = ref.watch(catalogProvider).value; if (id == null || catalog == null) return null; return catalog.trackById(id); }); final playerControllerProvider = Provider((ref) => PlayerController(ref)); class PlayerController { PlayerController(this._ref); final Ref _ref; NkAudioHandler get _handler => _ref.read(audioHandlerProvider); MediaItem toMediaItem(Track t) { final catalog = _ref.read(catalogProvider).requireValue; return MediaItem( id: t.id, title: t.titleLatin, artist: catalog.creditLine(t), album: t.occasion.label, duration: t.duration == Duration.zero ? null : t.duration, artUri: t.coverUrl == null ? null : Uri.parse(t.coverUrl!), extras: {'url': t.audioUrl, 'coverAsset': t.coverAsset, 'occasion': t.occasion.id}, ); } Future playTracks(List tracks, {int index = 0, bool shuffle = false}) async { await _handler.setShuffleMode(AudioServiceShuffleMode.none); await _handler.playQueue(tracks.map(toMediaItem).toList(), index: index); if (shuffle) await _handler.setShuffleMode(AudioServiceShuffleMode.all); } /// Plays downloaded songs from their saved details, so it works with no internet. Future playDownloads(List entries, {int index = 0}) async { await _handler.setShuffleMode(AudioServiceShuffleMode.none); await _handler.playQueue([ for (final e in entries) MediaItem( id: e.trackId, title: e.title, artist: e.credit, album: e.album, duration: e.durationMs == 0 ? null : Duration(milliseconds: e.durationMs), extras: {'url': '', 'occasion': e.occasion}, ), ], index: index); } void download(Iterable tracks) { final catalog = _ref.read(catalogProvider).requireValue; final store = _ref.read(downloadStoreProvider); for (final t in tracks) { store.enqueue(t, credit: catalog.creditLine(t), album: catalog.albumOf(t)?.title); } } Future playNext(Track t) => _handler.playNext(toMediaItem(t)); Future addToQueue(Track t) => _handler.addQueueItem(toMediaItem(t)); Future togglePlay() { final playing = _ref.read(playbackStateProvider).value?.playing ?? false; return playing ? _handler.pause() : _handler.play(); } Future toggleShuffle() { final on = _ref.read(playbackStateProvider).value?.shuffleMode != AudioServiceShuffleMode.none; return _handler.setShuffleMode(on ? AudioServiceShuffleMode.none : AudioServiceShuffleMode.all); } /// Off -> all -> one -> off. Future cycleRepeat() { final mode = _ref.read(playbackStateProvider).value?.repeatMode ?? AudioServiceRepeatMode.none; return _handler.setRepeatMode(switch (mode) { AudioServiceRepeatMode.none => AudioServiceRepeatMode.all, AudioServiceRepeatMode.all || AudioServiceRepeatMode.group => AudioServiceRepeatMode.one, AudioServiceRepeatMode.one => AudioServiceRepeatMode.none, }); } }